> ## Documentation Index
> Fetch the complete documentation index at: https://docs.nvisy.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Get redaction review

> Returns the redaction's review audit: the detection analysis with the reviewer's edits applied and the per-entity redaction outcome recorded.



## OpenAPI

````yaml /api-reference/openapi.json get /workspaces/{workspaceId}/redactions/{redactionId}/review
openapi: 3.1.0
info:
  title: Nvisy API
  summary: Document detection and redaction platform
  description: >-
    Nvisy provides intelligent detection of sensitive content in documents,
    redaction pipelines, and reviewer workflows. This API enables document
    upload, detection analysis, redaction, and collaborative review across your
    workspaces.
  termsOfService: https://nvisy.com/legal/terms-of-service
  contact:
    name: Nvisy Support
    url: https://nvisy.com
    email: hello@nvisy.com
  license:
    name: Apache-2.0
    url: https://www.apache.org/licenses/LICENSE-2.0
  version: 0.1.0
servers: []
security: []
tags:
  - name: Health
    description: Service health checks
  - name: Capabilities
    description: Read-only reference data describing this deployment
  - name: Accounts
    description: Account management and profile operations
  - name: Identities
    description: 'Account sign-in methods: password and linked providers'
  - name: Avatars
    description: Public avatar image serving
  - name: Authentication
    description: Login, signup, and token management
  - name: Workspaces
    description: Workspace creation and management
  - name: Activities
    description: Workspace activity log and export
  - name: Documents
    description: Document upload, download, and management
  - name: Analytics
    description: Aggregate metrics over a workspace's documents
  - name: Members
    description: Workspace member management
  - name: Invites
    description: Workspace invitation handling
  - name: API Tokens
    description: API token management
  - name: Connections
    description: External provider connections
  - name: Connection Syncs
    description: Connection synchronization runs
  - name: Providers
    description: Inference-provider configuration
  - name: Pipelines
    description: Redaction pipeline configuration
  - name: Detections
    description: Detection analysis and redaction
  - name: Redactions
    description: Redactions produced from a detection, with reviewer edits
  - name: Reviews
    description: Document reviews, the review queue, and their discussion
  - name: Policies
    description: Redaction policy configuration
  - name: Webhooks
    description: Webhook configuration
  - name: Notifications
    description: Account notification management
paths:
  /workspaces/{workspaceId}/redactions/{redactionId}/review:
    get:
      tags:
        - Redactions
      summary: Get redaction review
      description: >-
        Returns the redaction's review audit: the detection analysis with the
        reviewer's edits applied and the per-entity redaction outcome recorded.
      parameters:
        - in: path
          name: workspaceId
          description: Workspace identifier.
          required: true
          schema:
            description: Workspace identifier.
            type: string
            format: uuid
          style: simple
        - in: path
          name: redactionId
          description: Opaque identifier of the redaction.
          required: true
          schema:
            $ref: '#/components/schemas/RedactionId'
            description: Opaque identifier of the redaction.
          style: simple
      responses:
        '200':
          description: >-
            What detection found in one document.


            Wraps elide's [`Report`] with the things elide does not model:

            the recognition [`DocumentContext`] the entities were scored

            against, how the document decoded, and what the pass cost.


            Reviewer edits are not here. They are the caller's own input,

            applied to the report before anonymize

            ([`EditSet::apply`](crate::entity::EditSet::apply)), so an audit

            carries what analysis found *as amended* rather than the

            amendments themselves.


            # Serialization


            [`Serialize`] but deliberately **not** `Deserialize`: a

            serialized report tags entity groups by modality *name*, so

            rebuilding one needs the registry [`Engine`] holds. Read an

            audit back with [`Engine::deserialize_audit`].


            # Schema


            Generate under the **serialize** contract

            ([`SchemaSettings::for_serialize`]). `usage` is

            `skip_serializing_if`, and only that contract marks it optional

            — `schema_for!` defaults to deserialize and declares it

            required, so a generated client would reject responses this

            crate really emits.


            [`Engine`]: super::Engine

            [`Engine::deserialize_audit`]: super::Engine::deserialize_audit

            [`Report`]: elide::Report

            [`SchemaSettings::for_serialize`]:
            schemars::generate::SchemaSettings::for_serialize
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Audit'
        '401':
          description: >-
            The serialized shape of an HTTP error: the inert wire/OpenAPI-schema
            view

            that [`Error`] renders to at the response boundary.


            It carries no builder logic — [`Error`] is the type handlers
            construct and

            thread through `Result`, and it builds an `ErrorResponse` directly
            in its

            `IntoResponse` impl. `context` and `status` are not part of the JSON
            body

            (`context` is logged, `status` sets the HTTP status line).


            [`Error`]: crate::response::Error
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '403':
          description: >-
            The serialized shape of an HTTP error: the inert wire/OpenAPI-schema
            view

            that [`Error`] renders to at the response boundary.


            It carries no builder logic — [`Error`] is the type handlers
            construct and

            thread through `Result`, and it builds an `ErrorResponse` directly
            in its

            `IntoResponse` impl. `context` and `status` are not part of the JSON
            body

            (`context` is logged, `status` sets the HTTP status line).


            [`Error`]: crate::response::Error
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '404':
          description: >-
            The serialized shape of an HTTP error: the inert wire/OpenAPI-schema
            view

            that [`Error`] renders to at the response boundary.


            It carries no builder logic — [`Error`] is the type handlers
            construct and

            thread through `Result`, and it builds an `ErrorResponse` directly
            in its

            `IntoResponse` impl. `context` and `status` are not part of the JSON
            body

            (`context` is logged, `status` sets the HTTP status line).


            [`Error`]: crate::response::Error
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '409':
          description: >-
            The serialized shape of an HTTP error: the inert wire/OpenAPI-schema
            view

            that [`Error`] renders to at the response boundary.


            It carries no builder logic — [`Error`] is the type handlers
            construct and

            thread through `Result`, and it builds an `ErrorResponse` directly
            in its

            `IntoResponse` impl. `context` and `status` are not part of the JSON
            body

            (`context` is logged, `status` sets the HTTP status line).


            [`Error`]: crate::response::Error
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
      security:
        - BearerAuth: []
components:
  schemas:
    RedactionId:
      description: Opaque redaction identifier (redaction_<uuid>).
      type: string
      pattern: >-
        ^redaction_[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$
    Audit:
      description: >-
        What detection found in one document.


        Wraps elide's [`Report`] with the things elide does not model:

        the recognition [`DocumentContext`] the entities were scored

        against, how the document decoded, and what the pass cost.


        Reviewer edits are not here. They are the caller's own input,

        applied to the report before anonymize

        ([`EditSet::apply`](crate::entity::EditSet::apply)), so an audit

        carries what analysis found *as amended* rather than the

        amendments themselves.


        # Serialization


        [`Serialize`] but deliberately **not** `Deserialize`: a

        serialized report tags entity groups by modality *name*, so

        rebuilding one needs the registry [`Engine`] holds. Read an

        audit back with [`Engine::deserialize_audit`].


        # Schema


        Generate under the **serialize** contract

        ([`SchemaSettings::for_serialize`]). `usage` is

        `skip_serializing_if`, and only that contract marks it optional

        — `schema_for!` defaults to deserialize and declares it

        required, so a generated client would reject responses this

        crate really emits.


        [`Engine`]: super::Engine

        [`Engine::deserialize_audit`]: super::Engine::deserialize_audit

        [`Report`]: elide::Report

        [`SchemaSettings::for_serialize`]:
        schemars::generate::SchemaSettings::for_serialize
      type: object
      properties:
        codec:
          description: |-
            How this document was decoded when it was analyzed.

            Carried back so anonymize decodes identically: the entity
            offsets below are stored against the first decode, and a
            differently-rendered second one would not line up.
          allOf:
            - $ref: '#/components/schemas/CodecParams'
        context:
          description: |-
            What the caller asserted when this document was analyzed:
            languages, jurisdictions, document tags.

            Carried back so [`Engine::anonymize`] compiles against the
            same vocabulary analyze used, and re-decodes under the same
            codec configuration, without the caller re-passing it.

            [`Engine::anonymize`]: super::Engine::anonymize
          allOf:
            - $ref: '#/components/schemas/DocumentContext'
        recognition:
          description: |-
            The vocabularies this request introduced: labels elide does
            not ship, and how they were found.

            Carried back for the same reason [`context`] is. Anonymize
            compiles its catalog afresh, and a custom label missing
            from it resolves to nothing — the entity would be detected
            and then silently not redacted.

            [`context`]: Audit::context
          type: array
          items:
            $ref: '#/components/schemas/Recognition'
        report:
          description: |-
            The detections: elide's own report, every document and the
            parts nested in one, each entity carrying its provenance
            chain.

            Edit it through [`Report`]'s own API — [`include_part`],
            [`suppress_part`], [`part_entities`] — for the decisions
            elide models.

            [`Report`]: elide::Report
            [`include_part`]: elide::Report::include_part
            [`suppress_part`]: elide::Report::suppress_part
            [`part_entities`]: elide::Report::part_entities
          allOf:
            - $ref: '#/components/schemas/Report'
        usage:
          description: |-
            What the analyze pass cost: one entry per recognizer and
            enricher that ran, each self-identifying by the name the
            deployment configured it under.

            Carried here rather than read off the report: elide derives
            usage during analysis and drops it when a report is rebuilt
            from the wire, so a host that bills on model spend would
            lose it on the round trip.
          allOf:
            - $ref: '#/components/schemas/UsageReport'
      required:
        - report
        - context
        - codec
        - usage
    ErrorResponse:
      description: >-
        The serialized shape of an HTTP error: the inert wire/OpenAPI-schema
        view

        that [`Error`] renders to at the response boundary.


        It carries no builder logic — [`Error`] is the type handlers construct
        and

        thread through `Result`, and it builds an `ErrorResponse` directly in
        its

        `IntoResponse` impl. `context` and `status` are not part of the JSON
        body

        (`context` is logged, `status` sets the HTTP status line).


        [`Error`]: crate::response::Error
      type: object
      properties:
        message:
          description: User-friendly error message safe for client display.
          type: string
        name:
          description: The error name/type identifier.
          type: string
      required:
        - name
        - message
    CodecParams:
      description: |-
        How the codec decodes this document.

        Defaults to the codec's own behaviour, so a caller with no
        opinion passes [`CodecParams::default`].
      type: object
      properties:
        rasterMode:
          description: |-
            How container formats carrying both a text layer and page
            images treat OCR.

            Defaults to [`RasterMode::Auto`], the codec's own behaviour.
          allOf:
            - $ref: '#/components/schemas/RasterMode'
          default:
            kind: auto
    DocumentContext:
      description: |-
        What a caller asserts about the document being processed.

        Everything defaults to empty, so a caller asserting nothing
        passes [`DocumentContext::default`] and lets the recognizers use
        their own defaults.
      type: object
      properties:
        countries:
          description: |-
            Jurisdictions the caller asserts apply.

            Read by policy predicates that vary by jurisdiction, so a
            rule can act on a document from one country and not another.
          type: array
          items:
            $ref: '#/components/schemas/CountryCode'
        languages:
          description: |-
            Languages the caller asserts the document is in.

            Recognizers that take a language hint use it; the
            language-detection enricher fills the gap when this is
            empty.
          allOf:
            - $ref: '#/components/schemas/Languages'
          default: []
        metadata:
          description: |-
            Free-form request context: document tags, request purpose,
            output audience. See elide's [`ScopeMetadata`].
          allOf:
            - $ref: '#/components/schemas/ScopeMetadata'
    Recognition:
      description: |-
        Labels a request introduces, and how to detect them.

        The two halves are independent, and only one pairing is an
        error.

        A label with no matcher is legitimate: it joins the catalog, a
        policy may scope it, and a reviewer may add entities under it
        through [`Edit::Add`]. Nothing detects it automatically — there
        is no matcher to look for it — which is the point when the
        label marks something only a human can recognise.

        A matcher with no label is refused, since it would detect into
        a vocabulary the catalog never held.

        [`Edit::Add`]: https://docs.rs/elide-review
      type: object
      properties:
        custom:
          description: |-
            Label schemas elide does not ship.

            Only for labels outside the shipped set: one whose id
            collides with a builtin is refused, since it would shadow
            elide's own definition.
          type: array
          items:
            $ref: '#/components/schemas/Label'
        matchers:
          description: |-
            How to detect the labels [`custom`] introduces.

            Each names a label this request declares. Compiled fresh
            per request, so a request introducing none costs nothing.

            [`custom`]: Self::custom
          type: array
          items:
            $ref: '#/components/schemas/CustomMatcher'
    Report:
      type: object
      properties:
        parts:
          type: array
          items:
            discriminator:
              propertyName: modality
            oneOf:
              - type: object
                properties:
                  entities:
                    type: array
                    items:
                      $ref: '#/components/schemas/TextEntity'
                  id:
                    type: array
                    items:
                      type: string
                    minItems: 1
                  modality:
                    type: string
                    const: text
                required:
                  - id
                  - modality
                  - entities
              - type: object
                properties:
                  entities:
                    type: array
                    items:
                      $ref: '#/components/schemas/ImageEntity'
                  id:
                    type: array
                    items:
                      type: string
                    minItems: 1
                  modality:
                    type: string
                    const: image
                required:
                  - id
                  - modality
                  - entities
              - type: object
                properties:
                  entities:
                    type: array
                    items:
                      $ref: '#/components/schemas/AudioEntity'
                  id:
                    type: array
                    items:
                      type: string
                    minItems: 1
                  modality:
                    type: string
                    const: audio
                required:
                  - id
                  - modality
                  - entities
              - type: object
                properties:
                  entities:
                    type: array
                    items:
                      $ref: '#/components/schemas/TabularEntity'
                  id:
                    type: array
                    items:
                      type: string
                    minItems: 1
                  modality:
                    type: string
                    const: tabular
                required:
                  - id
                  - modality
                  - entities
        usage:
          $ref: '#/components/schemas/UsageReport'
      required:
        - parts
    UsageReport:
      description: >-
        Every recognizer/enricher's [`Usage`] across a whole document analysis,
        in

        the order the components ran (the body first, then each part).
      type: object
      properties:
        entries:
          description: The per-component usage entries, each self-identifying via its `id`.
          type: array
          items:
            $ref: '#/components/schemas/Usage'
      required:
        - entries
    RasterMode:
      description: >-
        Policy for rendering a document's pages to images.


        [`Auto`] is the default: use the text layer where it exists (redact by

        deleting glyphs) and render only where it is absent. [`Always`] renders

        every page regardless of the text layer, for documents whose text is

        missing, garbled, or a watermark. [`Never`] relies on the text layer
        only.


        Serializes with an internal `kind` tag (`{"kind": "auto"}`,

        `{"kind": "always", "dpi": 300}`, `{"kind": "never"}`).


        [`Auto`]: RasterMode::Auto

        [`Always`]: RasterMode::Always

        [`Never`]: RasterMode::Never
      oneOf:
        - description: >-
            Use the text layer where it exists and render pages only where it is

            absent. The default. (The text-layer-vs-scanned decision defers to
            the

            text path today; per-page rendering of scanned pages lands with
            OCR.)
          type: object
          properties:
            kind:
              type: string
              const: auto
          required:
            - kind
        - description: >-
            Always render pages to images at the given [`Dpi`], ignoring any
            text

            layer. For documents whose text layer is missing, garbled, or a

            watermark.
          type: object
          properties:
            dpi:
              description: Resolution to render pages at; [`Dpi::OCR`] (300) is typical.
              allOf:
                - $ref: '#/components/schemas/Dpi'
            kind:
              type: string
              const: always
          required:
            - kind
            - dpi
        - description: 'Never render: rely on the text layer only, even if it is absent.'
          type: object
          properties:
            kind:
              type: string
              const: never
          required:
            - kind
    CountryCode:
      description: |-
        [ISO 3166-1] country, identified by its code.

        Wraps [`celes::Country`], a static table entry carrying the numeric,
        alpha-2, and alpha-3 codes together with the country's name. Because
        every value comes from that fixed table, a `CountryCode` is always a
        real, recognised country; there is no way to hold an invalid one.

        Used to scope region-sensitive recognizers (a phone-number or
        national-id pattern, say) to the country whose format they target.

        Serializes as its alpha-2 code (e.g. `"US"`).

        [ISO 3166-1]: https://www.iso.org/iso-3166-country-codes.html
      type: string
    Languages:
      description: |-
        List of [`Language`]s resolved for one text scan.

        Built by a detector (one entry per detected region) or by the caller
        asserting languages. Carried on a [`RecognizerContext`] so every
        recognizer and the context enhancer can consult the call's languages.

        [`RecognizerContext`]: crate::recognition::RecognizerContext
      type: array
      items:
        $ref: '#/components/schemas/Language'
    ScopeMetadata:
      description: >-
        Caller-asserted scope shared across every payload of one analysis.


        Built once with the `with_*` chain and passed by reference to the

        analyzer, which borrows it into a fresh [`RecognizerContext`] per

        payload. It holds only what the *caller* asserts about the analysis as a

        whole, languages, jurisdictions, document labels, the target catalog, a

        correlation id, none of which depends on the medium, so one [`Scope`]

        drives a text, image, or audio analysis alike.


        Per-medium regions (caller-supplied inclusions and exclusions, which are

        `M::Location`-typed) live in [`Annotations`], attached to the analyzer

        of that modality. The per-payload working state (NLP artifacts, detected

        languages) lives on the context, not here.


        [`RecognizerContext`]: super::RecognizerContext

        [`Annotations`]: super::annotation::Annotations

        Free-form, caller-asserted request context: the *document* it is about
        and

        the *request* driving it.


        Three axes of opaque classification strings elide neither ships nor

        interprets, a downstream policy layer chooses what `"medical"` or

        `"fraud_detection"` or `"auditor"` mean. They are read in two places: a

        recognizer may bias its detection on them (the LLM prompt lists them so
        the

        model attends to the right terms), and a scope-aware operator predicate
        may

        branch on them at selection time (redact the same document differently
        per

        [`audience`]).


        - [`tags`] classify the *document* (`"medical"`, `"gdpr-request"`).

        - [`purpose`] is why the request exists (`"fraud_detection"`).

        - [`audience`] is who the redacted output is for (`"support_agent"`,
          `"auditor"`), the axis PCI-style "same document, two masks" branches on.

        [`tags`]: Self::tags

        [`purpose`]: Self::purpose

        [`audience`]: Self::audience
      type: object
      properties:
        audience:
          description: >-
            Who the redacted output is for (e.g. `"support_agent"`,
            `"auditor"`).

            The axis a per-audience redaction branches on: one detected
            document,

            selected differently per audience. May hold several.
          type: array
          default: []
          items:
            type: string
        purpose:
          description: >-
            The caller-asserted business purpose driving this request (e.g.

            `"fraud_detection"`, `"gdpr_erasure_request"`). A scope-aware
            operator

            predicate may skip or swap a rule based on it; a recognizer may bias

            detection on it. `None` when the caller asserts no purpose.
          type: string
        tags:
          description: |-
            Document-level classification tags (e.g. `"medical"`,
            `"gdpr-request"`). Recognizers may use these to bias their behavior
            for domain-specific terms; those that don't ignore the field.

            Named `tags`, not `labels`, to keep "label" reserved for the entity
            taxonomy ([`LabelRef`]/[`LabelCatalog`]): these classify the
            *document*, whereas the scope's catalog names the entity *types* to
            emit.

            [`LabelRef`]: crate::entity::LabelRef
            [`LabelCatalog`]: crate::entity::LabelCatalog
          type: array
          default: []
          items:
            type: string
    Label:
      description: >-
        Kind of sensitive information: a stable [`id`], per-language

        [`LabelLocale`]s, an optional [`category`], and zero or more tags.


        # Identity


        Labels are identified by [`id`], a stable lowercase `snake_case`

        string (`"phone_number"`), never localized, and the catalog key that a

        [`LabelRef`] resolves through. Selectors match by id. Derived equality

        is *structural*: two labels with the same id but different

        localizations or tags are not `==`; compare [`id`] for identity.


        # Localization


        The display name and description are localized per [`LanguageTag`].

        English (`"en"`) is required at construction and is the fallback when a

        requested locale is absent, so [`localization`] always returns some

        text, NER and LLM read the analysis language's name and description to

        prompt the model, keyed by the stable id.


        # Category and tags


        [`category`] is the single coarse group a label belongs to (`financial`,

        `health`, `identity`, …), for organizing detected entities by kind.
        Built-in

        labels ship with one; a custom label has none unless set.


        [`tags`] is a free-form list of *cross-cutting* markers a policy
        selector

        matches against, sensitivity flags a label may carry several of (`pii`,

        `phi`, `pci`, `sad`, `secret`). Distinct from the category: a label has
        at

        most one category but any number of tags. Custom labels can ship with
        zero

        of either.


        [`id`]: Label::id

        [`localization`]: Label::localization

        [`category`]: Label::category

        [`tags`]: Label::tags
      type: object
      properties:
        category:
          $ref: '#/components/schemas/Category'
        id:
          type: string
        localizations:
          $ref: '#/components/schemas/LocalizedText'
        tags:
          type: array
          items:
            type: string
      required:
        - id
        - localizations
        - tags
    CustomMatcher:
      description: |-
        How to detect one caller-authored label.

        Names a label the same request declares in [`custom`]. A
        matcher for a shipped built-in is rejected: elide already
        detects those, and two definitions for one label would race in
        reconciliation.

        [`custom`]: super::Recognition::custom
      type: object
      properties:
        confidence:
          description: |-
            Score stamped on every match. Defaults to `0.6`.

            Reconciliation keeps the higher-confidence entity when two
            detections overlap, so this is what decides whether a
            custom match beats a built-in one covering the same span.
          type: number
          format: float
          default: 0.6000000238418579
        label:
          description: |-
            The label this detects, which the request must declare in
            [`custom`](super::Recognition::custom).
          allOf:
            - $ref: '#/components/schemas/LabelRef'
        name:
          description: |-
            Human-readable name, recorded in the audit as the
            recognizer that found the entity, so a trail distinguishes
            a caller's matcher from a shipped pattern.
          type: string
      oneOf:
        - description: |-
            A regular expression over the decoded text.

            Compiled per request against a size limit. The `regex`
            crate does not backtrack, so a pathological pattern costs
            compile time rather than match time.
          type: object
          properties:
            kind:
              type: string
              const: pattern
            pattern:
              description: The expression, in `regex` crate syntax.
              type: string
          required:
            - kind
            - pattern
        - description: |-
            A literal term list, matched whole-word.

            For the vocabulary a regex describes badly: client names,
            internal codenames, a fixed roster of identifiers.
          type: object
          properties:
            kind:
              type: string
              const: terms
            terms:
              description: The literals to scan for.
              type: array
              items:
                type: string
          required:
            - kind
            - terms
      required:
        - label
        - name
    TextEntity:
      description: |-
        Detected piece of sensitive information within some medium.

        Generic over the [`Modality`] `M`, which is what makes the model
        multimodal: a text pipeline yields `Entity<Text>`, an audio pipeline
        `Entity<Audio>`, and so on. The entity's location is the modality's
        [`Location`] type, `M::Location`.

        # Birth and fusion

        A recognizer emits an entity directly, carrying a single recognition
        [`AuditEvent`] (its own finding) in the entity's [`audit`] trail. When
        several recognizers find the same thing, a fusion step (in
        `elide`) combines their entities into one: the survivor's
        [`location`] and [`confidence`] are the *fused* values, and every
        contributing recognition event, plus a deduplication event, is
        retained in its audit trail. The entity therefore carries its full
        audit trail with it.

        [`Location`]: Modality::Location
        [`AuditEvent`]: crate::entity::audit::AuditEvent
        [`audit`]: Entity::audit
        [`location`]: Entity::location
        [`confidence`]: Entity::confidence
      type: object
      properties:
        audit:
          description: >-
            Tamper-evident audit trail: every contributing detection, the fusion

            event if any, and the redaction that hid it, as a hash-linked DAG.
            It is

            also the single source of truth for whether a reviewer

            [suppressed](Self::is_suppressed) the entity, a suppression is a

            [`Manual`] event on this trail, not a separate flag.


            [`Manual`]: crate::entity::audit::AuditKind::Manual
          allOf:
            - $ref: '#/components/schemas/TextAuditLog'
        confidence:
          description: Effective confidence in `0.0..=1.0` (fused, if applicable).
          allOf:
            - $ref: '#/components/schemas/Confidence'
        coref:
          $ref: '#/components/schemas/EntityCoRef'
          description: |-
            Coreference identifier, if a recognizer resolved this entity as one
            mention of a cluster. Entities sharing an [`EntityCoRef`] denote the
            same real-world thing.
        id:
          description: |-
            Stable unique identity for this entity (time-ordered UUIDv7), minted
            when the entity is assembled.
          type: string
          format: uuid
        label:
          description: |-
            What kind of sensitive information this is (resolved via a
            [`LabelCatalog`]).
          allOf:
            - $ref: '#/components/schemas/LabelRef'
        language:
          description: |-
            The language of this entity's surrounding text, when a recognizer
            resolved one. `None` when unknown or language-agnostic.
          type: string
        location:
          description: >-
            Location of the entity within the medium (fused, if it came from
            more

            than one detection).
          allOf:
            - $ref: '#/components/schemas/TextLocation'
        recognized_range:
          $ref: '#/components/schemas/Range_of_uint'
          description: >-
            Byte range of the match in the *recognized text* it was found in
            (the

            OCR layout text, the audio transcript, or the text payload itself),

            the stable key back into that enrichment artifact, where the rich

            context lives (which OCR block, which speaker) that the geometric

            [`location`] cannot hold. `None` for entities not found via text

            recognition (e.g. a VLM box). An audit key, not a coordinate:
            redaction

            uses [`location`]; an audit uses this with the artifact.


            [`location`]: Entity::location
      required:
        - id
        - label
        - location
        - confidence
        - audit
    ImageEntity:
      description: |-
        Detected piece of sensitive information within some medium.

        Generic over the [`Modality`] `M`, which is what makes the model
        multimodal: a text pipeline yields `Entity<Text>`, an audio pipeline
        `Entity<Audio>`, and so on. The entity's location is the modality's
        [`Location`] type, `M::Location`.

        # Birth and fusion

        A recognizer emits an entity directly, carrying a single recognition
        [`AuditEvent`] (its own finding) in the entity's [`audit`] trail. When
        several recognizers find the same thing, a fusion step (in
        `elide`) combines their entities into one: the survivor's
        [`location`] and [`confidence`] are the *fused* values, and every
        contributing recognition event, plus a deduplication event, is
        retained in its audit trail. The entity therefore carries its full
        audit trail with it.

        [`Location`]: Modality::Location
        [`AuditEvent`]: crate::entity::audit::AuditEvent
        [`audit`]: Entity::audit
        [`location`]: Entity::location
        [`confidence`]: Entity::confidence
      type: object
      properties:
        audit:
          description: >-
            Tamper-evident audit trail: every contributing detection, the fusion

            event if any, and the redaction that hid it, as a hash-linked DAG.
            It is

            also the single source of truth for whether a reviewer

            [suppressed](Self::is_suppressed) the entity, a suppression is a

            [`Manual`] event on this trail, not a separate flag.


            [`Manual`]: crate::entity::audit::AuditKind::Manual
          allOf:
            - $ref: '#/components/schemas/ImageAuditLog'
        confidence:
          description: Effective confidence in `0.0..=1.0` (fused, if applicable).
          allOf:
            - $ref: '#/components/schemas/Confidence'
        coref:
          $ref: '#/components/schemas/EntityCoRef'
          description: |-
            Coreference identifier, if a recognizer resolved this entity as one
            mention of a cluster. Entities sharing an [`EntityCoRef`] denote the
            same real-world thing.
        id:
          description: |-
            Stable unique identity for this entity (time-ordered UUIDv7), minted
            when the entity is assembled.
          type: string
          format: uuid
        label:
          description: |-
            What kind of sensitive information this is (resolved via a
            [`LabelCatalog`]).
          allOf:
            - $ref: '#/components/schemas/LabelRef'
        language:
          description: |-
            The language of this entity's surrounding text, when a recognizer
            resolved one. `None` when unknown or language-agnostic.
          type: string
        location:
          description: >-
            Location of the entity within the medium (fused, if it came from
            more

            than one detection).
          allOf:
            - $ref: '#/components/schemas/ImageLocation'
        recognized_range:
          $ref: '#/components/schemas/Range_of_uint'
          description: >-
            Byte range of the match in the *recognized text* it was found in
            (the

            OCR layout text, the audio transcript, or the text payload itself),

            the stable key back into that enrichment artifact, where the rich

            context lives (which OCR block, which speaker) that the geometric

            [`location`] cannot hold. `None` for entities not found via text

            recognition (e.g. a VLM box). An audit key, not a coordinate:
            redaction

            uses [`location`]; an audit uses this with the artifact.


            [`location`]: Entity::location
      required:
        - id
        - label
        - location
        - confidence
        - audit
    AudioEntity:
      description: |-
        Detected piece of sensitive information within some medium.

        Generic over the [`Modality`] `M`, which is what makes the model
        multimodal: a text pipeline yields `Entity<Text>`, an audio pipeline
        `Entity<Audio>`, and so on. The entity's location is the modality's
        [`Location`] type, `M::Location`.

        # Birth and fusion

        A recognizer emits an entity directly, carrying a single recognition
        [`AuditEvent`] (its own finding) in the entity's [`audit`] trail. When
        several recognizers find the same thing, a fusion step (in
        `elide`) combines their entities into one: the survivor's
        [`location`] and [`confidence`] are the *fused* values, and every
        contributing recognition event, plus a deduplication event, is
        retained in its audit trail. The entity therefore carries its full
        audit trail with it.

        [`Location`]: Modality::Location
        [`AuditEvent`]: crate::entity::audit::AuditEvent
        [`audit`]: Entity::audit
        [`location`]: Entity::location
        [`confidence`]: Entity::confidence
      type: object
      properties:
        audit:
          description: >-
            Tamper-evident audit trail: every contributing detection, the fusion

            event if any, and the redaction that hid it, as a hash-linked DAG.
            It is

            also the single source of truth for whether a reviewer

            [suppressed](Self::is_suppressed) the entity, a suppression is a

            [`Manual`] event on this trail, not a separate flag.


            [`Manual`]: crate::entity::audit::AuditKind::Manual
          allOf:
            - $ref: '#/components/schemas/AudioAuditLog'
        confidence:
          description: Effective confidence in `0.0..=1.0` (fused, if applicable).
          allOf:
            - $ref: '#/components/schemas/Confidence'
        coref:
          $ref: '#/components/schemas/EntityCoRef'
          description: |-
            Coreference identifier, if a recognizer resolved this entity as one
            mention of a cluster. Entities sharing an [`EntityCoRef`] denote the
            same real-world thing.
        id:
          description: |-
            Stable unique identity for this entity (time-ordered UUIDv7), minted
            when the entity is assembled.
          type: string
          format: uuid
        label:
          description: |-
            What kind of sensitive information this is (resolved via a
            [`LabelCatalog`]).
          allOf:
            - $ref: '#/components/schemas/LabelRef'
        language:
          description: |-
            The language of this entity's surrounding text, when a recognizer
            resolved one. `None` when unknown or language-agnostic.
          type: string
        location:
          description: >-
            Location of the entity within the medium (fused, if it came from
            more

            than one detection).
          allOf:
            - $ref: '#/components/schemas/AudioLocation'
        recognized_range:
          $ref: '#/components/schemas/Range_of_uint'
          description: >-
            Byte range of the match in the *recognized text* it was found in
            (the

            OCR layout text, the audio transcript, or the text payload itself),

            the stable key back into that enrichment artifact, where the rich

            context lives (which OCR block, which speaker) that the geometric

            [`location`] cannot hold. `None` for entities not found via text

            recognition (e.g. a VLM box). An audit key, not a coordinate:
            redaction

            uses [`location`]; an audit uses this with the artifact.


            [`location`]: Entity::location
      required:
        - id
        - label
        - location
        - confidence
        - audit
    TabularEntity:
      description: |-
        Detected piece of sensitive information within some medium.

        Generic over the [`Modality`] `M`, which is what makes the model
        multimodal: a text pipeline yields `Entity<Text>`, an audio pipeline
        `Entity<Audio>`, and so on. The entity's location is the modality's
        [`Location`] type, `M::Location`.

        # Birth and fusion

        A recognizer emits an entity directly, carrying a single recognition
        [`AuditEvent`] (its own finding) in the entity's [`audit`] trail. When
        several recognizers find the same thing, a fusion step (in
        `elide`) combines their entities into one: the survivor's
        [`location`] and [`confidence`] are the *fused* values, and every
        contributing recognition event, plus a deduplication event, is
        retained in its audit trail. The entity therefore carries its full
        audit trail with it.

        [`Location`]: Modality::Location
        [`AuditEvent`]: crate::entity::audit::AuditEvent
        [`audit`]: Entity::audit
        [`location`]: Entity::location
        [`confidence`]: Entity::confidence
      type: object
      properties:
        audit:
          description: >-
            Tamper-evident audit trail: every contributing detection, the fusion

            event if any, and the redaction that hid it, as a hash-linked DAG.
            It is

            also the single source of truth for whether a reviewer

            [suppressed](Self::is_suppressed) the entity, a suppression is a

            [`Manual`] event on this trail, not a separate flag.


            [`Manual`]: crate::entity::audit::AuditKind::Manual
          allOf:
            - $ref: '#/components/schemas/TabularAuditLog'
        confidence:
          description: Effective confidence in `0.0..=1.0` (fused, if applicable).
          allOf:
            - $ref: '#/components/schemas/Confidence'
        coref:
          $ref: '#/components/schemas/EntityCoRef'
          description: |-
            Coreference identifier, if a recognizer resolved this entity as one
            mention of a cluster. Entities sharing an [`EntityCoRef`] denote the
            same real-world thing.
        id:
          description: |-
            Stable unique identity for this entity (time-ordered UUIDv7), minted
            when the entity is assembled.
          type: string
          format: uuid
        label:
          description: |-
            What kind of sensitive information this is (resolved via a
            [`LabelCatalog`]).
          allOf:
            - $ref: '#/components/schemas/LabelRef'
        language:
          description: |-
            The language of this entity's surrounding text, when a recognizer
            resolved one. `None` when unknown or language-agnostic.
          type: string
        location:
          description: >-
            Location of the entity within the medium (fused, if it came from
            more

            than one detection).
          allOf:
            - $ref: '#/components/schemas/TabularLocation'
        recognized_range:
          $ref: '#/components/schemas/Range_of_uint'
          description: >-
            Byte range of the match in the *recognized text* it was found in
            (the

            OCR layout text, the audio transcript, or the text payload itself),

            the stable key back into that enrichment artifact, where the rich

            context lives (which OCR block, which speaker) that the geometric

            [`location`] cannot hold. `None` for entities not found via text

            recognition (e.g. a VLM box). An audit key, not a coordinate:
            redaction

            uses [`location`]; an audit uses this with the artifact.


            [`location`]: Entity::location
      required:
        - id
        - label
        - location
        - confidence
        - audit
    Usage:
      description: >-
        One recognizer's or enricher's resource usage for one payload.


        `id` and `duration` are always present; `count` (entities or spans
        found,

        artifacts produced) and `model` are present only where they mean

        something. `duration` serializes as a whole number of milliseconds.
      type: object
      properties:
        count:
          description: >-
            Entities or spans found (recognizer) or artifacts produced
            (enricher);

            `None` when a count is not meaningful for the component.
          type: integer
          format: uint64
          minimum: 0
        duration:
          description: Wall-clock execution time. Serialized as integer milliseconds.
          type: integer
          format: uint64
          minimum: 0
        id:
          description: Which recognizer / enricher this describes.
          allOf:
            - $ref: '#/components/schemas/RecognizerId'
        model:
          $ref: '#/components/schemas/ModelUsage'
          description: Model / token detail; `None` for a pure-CPU component.
      required:
        - id
        - duration
    Dpi:
      description: |-
        Dots-per-inch resolution for rasterizing vector content.

        Used e.g. for rendering PDF pages to images for OCR.

        PDF coordinates are in points (1 pt = 1/72 in), so
        [`scale_factor`] gives the multiplier from points to
        pixels at this resolution.

        [`scale_factor`]: Self::scale_factor
      type: integer
      format: uint16
      maximum: 65535
      minimum: 0
    Language:
      description: |-
        Single language detection result.

        Carries the language plus an optional confidence and an optional
        byte-offset [`LanguageSpan`]. Backends that don't expose confidence
        leave it `None`; single-language detectors that don't track per-region
        information leave `span` as `None`. The `provenance` field records
        whether the answer came from a detector or was asserted by the caller.
      type: object
      properties:
        confidence:
          $ref: '#/components/schemas/Confidence'
          description: Optional confidence score. `None` when not exposed.
        language:
          description: Language.
          allOf:
            - $ref: '#/components/schemas/LanguageTag'
        provenance:
          description: 'How this language was obtained: detected or caller-asserted.'
          allOf:
            - $ref: '#/components/schemas/LanguageProvenance'
        span:
          $ref: '#/components/schemas/LanguageSpan'
          description: |-
            Byte-offset range this detection applies to, when known. `None`
            means the whole text.
      required:
        - language
        - provenance
    Category:
      description: >-
        The coarse group a [`Label`] belongs to, for organizing detected
        entities

        by kind (`financial`, `health`, `identity`, …).


        A category answers "what *sort* of information is this" at a display
        level:

        a consumer groups a redaction audit into sections by category. It is

        distinct from a label's [`tags`], which are cross-cutting sensitivity

        markers (`pii`, `phi`, `pci`) a label may carry several of; a label has
        at

        most **one** category. Built-in labels ship with a category; a custom

        [`Label`] has none unless one is set.


        The value is an open, lowercase `snake_case` identifier, so a custom
        label

        can define its own category rather than being confined to the shipped
        set.


        [`Label`]: super::Label

        [`tags`]: super::Label::tags
      type: string
    LocalizedText:
      description: |-
        A value localized per [`LanguageTag`], with an English-first fallback.

        The reusable mechanism behind any text that varies by language: a
        [`Label`]'s display name and description, a redaction operator's bucket
        label, and so on. English (`"en"`) is the conventional anchor,
        constructors seed it, and [`resolve`] falls back to it (then to any
        entry) when a requested locale is absent, so a caller that supplied
        English always gets *some* value.

        Generic over the stored value `T`, so it carries a bare `HipStr`
        (a bucket label) or a richer struct (a label's name-plus-description)
        equally. It is a thin wrapper over a `HashMap<LanguageTag, T>`; the
        added value is the fallback policy in [`resolve`], kept in one place
        rather than reimplemented at each use site.

        [`Label`]: crate::entity::Label
        [`resolve`]: LocalizedText::resolve
      type: object
      additionalProperties:
        $ref: '#/components/schemas/LabelLocale'
    LabelRef:
      description: |-
        Lightweight reference to a [`Label`], carrying only its id.

        This is what detections and entities hold: cloning is cheap (short
        ids inline into the [`HipStr`]), and the full [`Label`], with its
        localized names and descriptions, is resolved on demand from a
        [`LabelCatalog`].

        [`Label`]: crate::entity::Label
        [`LabelCatalog`]: crate::entity::LabelCatalog
      type: string
    TextAuditLog:
      description: >-
        Full audit trail of an [`Entity`]: every [`AuditEvent`] in its life, as
        a

        tamper-evident DAG.


        Where Presidio keeps a shallow, optional, per-stage explanation that is

        stripped by default, an `AuditLog` is always present and records the

        entity's *entire* life: each recognizer that found it, the deduplication

        that fused them, any confidence calibration, and the redaction that hid
        it.

        Nothing is collapsed: every recognizer keeps its own recognition event
        with

        its location and score.


        The events form a **directed acyclic graph**, not a flat list. A
        recognizer

        records a birth event (no parents); each later step links to the event
        it

        follows; and a [fusion](Self::record_fusion) links to *several* parents
        at

        once: the heads of the trails it combines. This is the true shape of a

        deduplicated entity: two recognizers are siblings, then a fusion joins
        them.

        The events are stored in the order they were recorded, which is a

        topological order of the DAG (a parent is always recorded before its

        children).


        Two chains ride the DAG's edges:


        - a **confidence chain**, where each event's [`confidence`] is the
        entity's
          effective score after it; the score flowing *in* is its parents'
          confidence, so [`final_confidence`] and the full history are recoverable;
        - a **hash chain**, where each event's [`hash`] folds its payload
        together
          with its parents' hashes, so any edit, reorder, insertion, or deletion of
          an earlier event breaks every event downstream. [`verify`] walks the DAG
          and reports the first break.

        [`Entity`]: crate::entity::Entity

        [`confidence`]: AuditEvent::confidence

        [`hash`]: AuditEvent::hash

        [`final_confidence`]: Self::final_confidence

        [`verify`]: Self::verify
      type: array
      items:
        $ref: '#/components/schemas/TextAuditEvent'
    Confidence:
      description: |-
        Confidence score in the closed range `0.0..=1.0`.

        Carried by every provenance [`AuditEvent`] (the `before`/`after` of a
        recognition, fusion, or calibration) and by the effective confidence
        of an [`Entity`]. The newtype enforces the range at construction so
        no downstream code has to defend against values outside `[0, 1]`.

        Distinct from [`ConfidenceThreshold`] so the two cannot be confused
        at a call site: a score is *produced* by detection, a threshold is a
        *cutoff* configured to filter scores. Compare the two with
        [`ConfidenceThreshold::passes`].

        [`AuditEvent`]: crate::entity::audit::AuditEvent
        [`Entity`]: crate::entity::Entity
      type: number
      format: float
    EntityCoRef:
      description: |-
        Coreference identifier shared by entities that denote the same
        real-world thing.

        Assigned by a recognizer that performs coreference resolution: an
        NER model or LLM that recognises "Alice", "she", and "Ms. Smith" as
        the same person within one detection call. Two entities carrying the
        same `EntityCoRef` are coreferent: they refer to the same underlying
        entity even though each is a distinct mention with its own span.

        The identifier is opaque and only meaningful *within* a single
        detection call: it is the recognizer's local handle for a cluster of
        mentions, not a global key.
      type: string
    TextLocation:
      description: >-
        Where an entity sits in text: a [coordinate](TextCoord) (a decoded byte
        range,

        or a source-only reference) plus an optional page number.


        The coordinate is either a [`Decoded`](TextCoord::Decoded) byte range in
        the

        pipeline's text stream, or a [`Source`](TextCoord::Source)-only
        reference for

        content with no decoded range (a reviewer selecting rendered text). The
        page

        is orthogonal to the coordinate kind, so it sits alongside rather than
        inside.


        Ordering and overlap consider only the coordinate; the page is carried
        for

        codecs that page their text but does not affect comparison.
      type: object
      properties:
        coord:
          description: 'The coordinate: a decoded range, or a source-only reference.'
          allOf:
            - $ref: '#/components/schemas/TextCoord'
        page:
          description: 1-based page number, when known. Orthogonal to the coordinate kind.
          type: integer
          format: uint32
          minimum: 0
      required:
        - coord
    Range_of_uint:
      type: object
      properties:
        end:
          type: integer
          format: uint
          minimum: 0
        start:
          type: integer
          format: uint
          minimum: 0
      required:
        - start
        - end
    ImageAuditLog:
      description: >-
        Full audit trail of an [`Entity`]: every [`AuditEvent`] in its life, as
        a

        tamper-evident DAG.


        Where Presidio keeps a shallow, optional, per-stage explanation that is

        stripped by default, an `AuditLog` is always present and records the

        entity's *entire* life: each recognizer that found it, the deduplication

        that fused them, any confidence calibration, and the redaction that hid
        it.

        Nothing is collapsed: every recognizer keeps its own recognition event
        with

        its location and score.


        The events form a **directed acyclic graph**, not a flat list. A
        recognizer

        records a birth event (no parents); each later step links to the event
        it

        follows; and a [fusion](Self::record_fusion) links to *several* parents
        at

        once: the heads of the trails it combines. This is the true shape of a

        deduplicated entity: two recognizers are siblings, then a fusion joins
        them.

        The events are stored in the order they were recorded, which is a

        topological order of the DAG (a parent is always recorded before its

        children).


        Two chains ride the DAG's edges:


        - a **confidence chain**, where each event's [`confidence`] is the
        entity's
          effective score after it; the score flowing *in* is its parents'
          confidence, so [`final_confidence`] and the full history are recoverable;
        - a **hash chain**, where each event's [`hash`] folds its payload
        together
          with its parents' hashes, so any edit, reorder, insertion, or deletion of
          an earlier event breaks every event downstream. [`verify`] walks the DAG
          and reports the first break.

        [`Entity`]: crate::entity::Entity

        [`confidence`]: AuditEvent::confidence

        [`hash`]: AuditEvent::hash

        [`final_confidence`]: Self::final_confidence

        [`verify`]: Self::verify
      type: array
      items:
        $ref: '#/components/schemas/ImageAuditEvent'
    ImageLocation:
      description: |-
        Region within image content.

        An axis-aligned [`BoundingBox`] in pixel coordinates locates the
        region; an optional [`Polygon`] captures a rotated or quadrilateral
        shape when the source produced one (OCR engines that emit 4-point
        polygons), and an optional page number addresses multi-page documents.
      type: object
      properties:
        bounding_box:
          description: Axis-aligned bounding box of the region, in pixel coordinates.
          allOf:
            - $ref: '#/components/schemas/BoundingBox'
        page:
          description: 1-based page number, for multi-page documents like PDFs.
          type: integer
          format: uint32
          minimum: 0
        polygon:
          $ref: '#/components/schemas/Polygon'
          description: |-
            Polygon vertices when the region is rotated or quadrilateral.
            Axis-aligned-only sources leave this unset.
      required:
        - bounding_box
    AudioAuditLog:
      description: >-
        Full audit trail of an [`Entity`]: every [`AuditEvent`] in its life, as
        a

        tamper-evident DAG.


        Where Presidio keeps a shallow, optional, per-stage explanation that is

        stripped by default, an `AuditLog` is always present and records the

        entity's *entire* life: each recognizer that found it, the deduplication

        that fused them, any confidence calibration, and the redaction that hid
        it.

        Nothing is collapsed: every recognizer keeps its own recognition event
        with

        its location and score.


        The events form a **directed acyclic graph**, not a flat list. A
        recognizer

        records a birth event (no parents); each later step links to the event
        it

        follows; and a [fusion](Self::record_fusion) links to *several* parents
        at

        once: the heads of the trails it combines. This is the true shape of a

        deduplicated entity: two recognizers are siblings, then a fusion joins
        them.

        The events are stored in the order they were recorded, which is a

        topological order of the DAG (a parent is always recorded before its

        children).


        Two chains ride the DAG's edges:


        - a **confidence chain**, where each event's [`confidence`] is the
        entity's
          effective score after it; the score flowing *in* is its parents'
          confidence, so [`final_confidence`] and the full history are recoverable;
        - a **hash chain**, where each event's [`hash`] folds its payload
        together
          with its parents' hashes, so any edit, reorder, insertion, or deletion of
          an earlier event breaks every event downstream. [`verify`] walks the DAG
          and reports the first break.

        [`Entity`]: crate::entity::Entity

        [`confidence`]: AuditEvent::confidence

        [`hash`]: AuditEvent::hash

        [`final_confidence`]: Self::final_confidence

        [`verify`]: Self::verify
      type: array
      items:
        $ref: '#/components/schemas/AudioAuditEvent'
    AudioLocation:
      description: |-
        A [`TimeSpan`] within audio content, with an optional speaker label.

        The time span is the coordinate; ordering and overlap consider only it.
        The optional [`speaker_id`] is a diarization label,
        not a coordinate: two utterances from different speakers at the same
        instant still overlap in time, so the speaker is carried for provenance
        but excluded from comparison.

        [`speaker_id`]: Self::speaker_id
      type: object
      properties:
        span:
          description: Time span the location covers, in the stream's timeline.
          allOf:
            - $ref: '#/components/schemas/TimeSpan'
        speaker_id:
          description: Diarization label of the speaker, when a diarizer assigned one.
          type: string
      required:
        - span
    TabularAuditLog:
      description: >-
        Full audit trail of an [`Entity`]: every [`AuditEvent`] in its life, as
        a

        tamper-evident DAG.


        Where Presidio keeps a shallow, optional, per-stage explanation that is

        stripped by default, an `AuditLog` is always present and records the

        entity's *entire* life: each recognizer that found it, the deduplication

        that fused them, any confidence calibration, and the redaction that hid
        it.

        Nothing is collapsed: every recognizer keeps its own recognition event
        with

        its location and score.


        The events form a **directed acyclic graph**, not a flat list. A
        recognizer

        records a birth event (no parents); each later step links to the event
        it

        follows; and a [fusion](Self::record_fusion) links to *several* parents
        at

        once: the heads of the trails it combines. This is the true shape of a

        deduplicated entity: two recognizers are siblings, then a fusion joins
        them.

        The events are stored in the order they were recorded, which is a

        topological order of the DAG (a parent is always recorded before its

        children).


        Two chains ride the DAG's edges:


        - a **confidence chain**, where each event's [`confidence`] is the
        entity's
          effective score after it; the score flowing *in* is its parents'
          confidence, so [`final_confidence`] and the full history are recoverable;
        - a **hash chain**, where each event's [`hash`] folds its payload
        together
          with its parents' hashes, so any edit, reorder, insertion, or deletion of
          an earlier event breaks every event downstream. [`verify`] walks the DAG
          and reports the first break.

        [`Entity`]: crate::entity::Entity

        [`confidence`]: AuditEvent::confidence

        [`hash`]: AuditEvent::hash

        [`final_confidence`]: Self::final_confidence

        [`verify`]: Self::verify
      type: array
      items:
        $ref: '#/components/schemas/TabularAuditEvent'
    TabularLocation:
      description: |-
        Cell-addressed location within tabular content.

        Identifies a cell by zero-based [`row_index`] and
        [`column_index`], optionally narrowed to a byte
        range within the cell text for an entity that spans only part of the
        cell. [`sheet_name`] scopes the cell to one sheet of
        a multi-sheet workbook; [`column_name`] is a
        human-readable header carried for provenance.

        Overlap and ordering treat the sheet, cell, and byte range as
        coordinates. The column name is redundant with the column index, so it
        is carried but excluded from comparison.

        [`row_index`]: Self::row_index
        [`column_index`]: Self::column_index
        [`sheet_name`]: Self::sheet_name
        [`column_name`]: Self::column_name
      type: object
      properties:
        column_index:
          description: Zero-based column index of the cell.
          type: integer
          format: uint32
          minimum: 0
        column_name:
          description: Header label of the column, when known.
          type: string
        end_offset:
          description: |-
            Byte offset within the cell text where the entity ends (exclusive).
            Unset means the whole cell.
          type: integer
          format: uint
          minimum: 0
        row_index:
          description: Zero-based row index of the cell.
          type: integer
          format: uint32
          minimum: 0
        sheet_name:
          description: Sheet name within a multi-sheet workbook, when known.
          type: string
        start_offset:
          description: |-
            Byte offset within the cell text where the entity starts. Unset
            means the whole cell.
          type: integer
          format: uint
          minimum: 0
      required:
        - row_index
        - column_index
    RecognizerId:
      description: |-
        Identifies a recognizer (name + version).

        Pairs a stable name with a free-form version string so the audit
        trail records not just *which* recognizer fired but *which build* of
        it: a rerun against an updated ruleset or model is then
        distinguishable from the original. The version is opaque text (a
        semver, a checkpoint hash, a ruleset date); the core attaches no
        ordering or comparison semantics to it.
      type: object
      properties:
        name:
          description: Stable, human-readable recognizer name (e.g. `"us-ssn-pattern"`).
          type: string
        version:
          description: Recognizer's version at the time it ran.
          type: string
      required:
        - name
        - version
    ModelUsage:
      description: >-
        The model a model-backed recognizer or enricher called, and its token
        cost.


        Absent from a pure-CPU component (a pattern recognizer, a language
        enricher).
      type: object
      properties:
        model:
          description: Model name the backend called (e.g. `"gpt-4o"`, `"gliner-multi"`).
          type: string
        tokens:
          description: Tokens the call spent, as far as the provider reports them.
          allOf:
            - $ref: '#/components/schemas/TokenCounts'
        version:
          description: Model version, when the backend reports one.
          type: string
      required:
        - model
    LanguageTag:
      description: |-
        Well-formed [BCP 47] language tag, such as `en`, `en-US`, or
        `zh-Hant-HK`.

        Wraps [`oxilangtag::LanguageTag`] over a [`HipStr`] backing store, so
        short tags (the overwhelming common case) stay inline without a heap
        allocation. Parsing validates the tag's structure up front; the
        newtype therefore guarantees that any `LanguageTag` value in the
        model is syntactically valid.

        Used to record the language a recognizer is scoped to, or the
        detected language of a span of content.

        [BCP 47]: https://www.rfc-editor.org/info/bcp47
      type: string
    LanguageProvenance:
      description: |-
        How a [`Language`]'s language was obtained.

        Lets consumers distinguish "a detector ran and got this answer" from
        "the caller asserted this language". An assertion may still carry an
        optional confidence, so this is independent of the confidence field.
      oneOf:
        - description: Produced by a language-detection backend.
          type: string
          const: detected
        - description: Asserted by the caller.
          type: string
          const: asserted
    LanguageSpan:
      description: |-
        Byte-offset range within the analyzed text.

        Attached to a [`Language`] when the detector knows the span
        its answer covers (mixed-language input produces multiple detections,
        each with a distinct span). Single-language detections from
        non-segmenting backends, and caller-asserted answers, typically leave
        the span as `None`.
      type: object
      properties:
        end:
          description: Byte offset of the span end in the original text.
          type: integer
          format: uint
          minimum: 0
        start:
          description: Byte offset of the span start in the original text.
          type: integer
          format: uint
          minimum: 0
      required:
        - start
        - end
    LabelLocale:
      description: |-
        A label's human-facing text in one language: a display name and an
        optional fuller description.

        The `name` is a short, natural-language phrase (`"phone number"`), the
        label a zero-shot NER model like GLiNER matches on, and the primary
        text an LLM prompt shows. The `description` is optional extra guidance
        for backends that consume it (GLiNER-2.0's bi-encoder, an LLM); leave
        it `None` when the name alone is clear.
      type: object
      properties:
        description:
          description: |-
            Optional fuller description, for description-capable backends
            (GLiNER-2.0, LLM). `None` when the name suffices.
          type: string
        name:
          description: |-
            Short natural-language display name (e.g. `"phone number"`). What a
            zero-shot NER model matches on and an LLM prompt surfaces.
          type: string
      required:
        - name
    TextAuditEvent:
      description: >-
        One node in an entity's audit DAG: a thing that happened, with its

        effect on confidence and its tamper-evident links.


        Events are recorded on an entity's [`AuditLog`], forming the full audit

        trail of its life: each recognizer that found it, the deduplication that

        fused them, any score calibration, and the redaction that hid it. The

        uniform spine (who, resulting score, when) is the same for every event;
        the

        [`kind`] carries the event-specific detail *and* the explanation of why
        the

        event happened.


        Each event links to its [`parents`] by their [`hash`]: a birth event (a

        recognizer's first detection) has no parents, a normal step has one, and
        a

        [fusion](crate::entity::audit::AuditLog::record_fusion) has several. Its
        own

        [`hash`] folds the

        payload together with the parents' hashes, so altering any event breaks

        every event downstream of it: [`AuditLog::verify`] walks the DAG and

        reports the first break.


        `entity.confidence` always equals the [`confidence`] of the most recent

        event. The confidence *flowing in* is not stored: it is the parents'

        [`confidence`], recovered from the DAG.


        [`AuditLog`]: crate::entity::audit::AuditLog

        [`AuditLog::record_fusion`]:
        crate::entity::audit::AuditLog::record_fusion

        [`AuditLog::verify`]: crate::entity::audit::AuditLog::verify

        [`kind`]: AuditEvent::kind

        [`parents`]: AuditEvent::parents

        [`hash`]: AuditEvent::hash

        [`confidence`]: AuditEvent::confidence
      type: object
      properties:
        confidence:
          description: >-
            Confidence after this event: the entity's effective confidence once
            it

            has happened.
          allOf:
            - $ref: '#/components/schemas/Confidence'
        hash:
          description: >-
            This event's hash, over its payload and its parents' hashes.
            Assigned by

            [`AuditLog`] when the event is recorded; read it through

            [`hash`](Self::hash). Recomputing it is how the DAG is verified.


            Not publicly settable: only [`AuditLog`] assigns it, so a caller
            cannot

            forge a self-consistent event outside the recording path.


            [`AuditLog`]: crate::entity::audit::AuditLog
          allOf:
            - $ref: '#/components/schemas/AuditHash'
        kind:
          description: Kind of event, with its event-specific detail and its rationale.
          allOf:
            - $ref: '#/components/schemas/TextAuditKind'
        parents:
          description: >-
            The events this one follows, by their hash. Empty for a birth event,
            one

            entry for a normal step, several for a fusion. Assigned by
            [`AuditLog`]

            when the event is recorded; read it through
            [`parents`](Self::parents).


            Not publicly settable: the links are the tamper-evident structure,
            so

            only [`AuditLog::record`] / [`AuditLog::record_fusion`] assign them.


            [`AuditLog`]: crate::entity::audit::AuditLog

            [`AuditLog::record`]: crate::entity::audit::AuditLog::record

            [`AuditLog::record_fusion`]:
            crate::entity::audit::AuditLog::record_fusion
          type: array
          items:
            $ref: '#/components/schemas/AuditHash'
        source:
          description: >-
            Who produced this event: a recognizer name, a deduplication
            strategy,

            an operator, or whatever acted.
          type: string
        timestamp:
          description: When the event happened (UTC).
          type: string
      required:
        - source
        - confidence
        - timestamp
        - kind
        - parents
        - hash
    TextCoord:
      description: >-
        The coordinate of a [`TextLocation`]: a decoded byte range, or a
        source-only

        reference.


        The distinction is whether a decoded range exists at all. A recognizer
        over

        decoded text produces a [`Decoded`](Self::Decoded) span (with the raw
        source

        it decodes from, when the codec's decoded text differs from the source).
        A

        reviewer marking rendered text has no decoded range, only where the
        selection

        sits in the raw bytes, a [`Source`](Self::Source).
      oneOf:
        - description: >-
            A decoded byte range in the pipeline's text stream, with the raw

            [`SourceRef`]s it came from when the codec's decoded text differs
            from the

            source (empty for plain text / CSV, where decoded == source).
          type: object
          properties:
            kind:
              type: string
              const: decoded
          allOf:
            - $ref: '#/components/schemas/DecodedSpan'
          required:
            - kind
        - description: >-
            Source-only: no decoded range (a reviewer selecting rendered text),
            only

            where it sits in the raw bytes. Non-empty by construction.
          type: object
          properties:
            kind:
              type: string
              const: source
          allOf:
            - $ref: '#/components/schemas/SourceSpan'
          required:
            - kind
    ImageAuditEvent:
      description: >-
        One node in an entity's audit DAG: a thing that happened, with its

        effect on confidence and its tamper-evident links.


        Events are recorded on an entity's [`AuditLog`], forming the full audit

        trail of its life: each recognizer that found it, the deduplication that

        fused them, any score calibration, and the redaction that hid it. The

        uniform spine (who, resulting score, when) is the same for every event;
        the

        [`kind`] carries the event-specific detail *and* the explanation of why
        the

        event happened.


        Each event links to its [`parents`] by their [`hash`]: a birth event (a

        recognizer's first detection) has no parents, a normal step has one, and
        a

        [fusion](crate::entity::audit::AuditLog::record_fusion) has several. Its
        own

        [`hash`] folds the

        payload together with the parents' hashes, so altering any event breaks

        every event downstream of it: [`AuditLog::verify`] walks the DAG and

        reports the first break.


        `entity.confidence` always equals the [`confidence`] of the most recent

        event. The confidence *flowing in* is not stored: it is the parents'

        [`confidence`], recovered from the DAG.


        [`AuditLog`]: crate::entity::audit::AuditLog

        [`AuditLog::record_fusion`]:
        crate::entity::audit::AuditLog::record_fusion

        [`AuditLog::verify`]: crate::entity::audit::AuditLog::verify

        [`kind`]: AuditEvent::kind

        [`parents`]: AuditEvent::parents

        [`hash`]: AuditEvent::hash

        [`confidence`]: AuditEvent::confidence
      type: object
      properties:
        confidence:
          description: >-
            Confidence after this event: the entity's effective confidence once
            it

            has happened.
          allOf:
            - $ref: '#/components/schemas/Confidence'
        hash:
          description: >-
            This event's hash, over its payload and its parents' hashes.
            Assigned by

            [`AuditLog`] when the event is recorded; read it through

            [`hash`](Self::hash). Recomputing it is how the DAG is verified.


            Not publicly settable: only [`AuditLog`] assigns it, so a caller
            cannot

            forge a self-consistent event outside the recording path.


            [`AuditLog`]: crate::entity::audit::AuditLog
          allOf:
            - $ref: '#/components/schemas/AuditHash'
        kind:
          description: Kind of event, with its event-specific detail and its rationale.
          allOf:
            - $ref: '#/components/schemas/ImageAuditKind'
        parents:
          description: >-
            The events this one follows, by their hash. Empty for a birth event,
            one

            entry for a normal step, several for a fusion. Assigned by
            [`AuditLog`]

            when the event is recorded; read it through
            [`parents`](Self::parents).


            Not publicly settable: the links are the tamper-evident structure,
            so

            only [`AuditLog::record`] / [`AuditLog::record_fusion`] assign them.


            [`AuditLog`]: crate::entity::audit::AuditLog

            [`AuditLog::record`]: crate::entity::audit::AuditLog::record

            [`AuditLog::record_fusion`]:
            crate::entity::audit::AuditLog::record_fusion
          type: array
          items:
            $ref: '#/components/schemas/AuditHash'
        source:
          description: >-
            Who produced this event: a recognizer name, a deduplication
            strategy,

            an operator, or whatever acted.
          type: string
        timestamp:
          description: When the event happened (UTC).
          type: string
      required:
        - source
        - confidence
        - timestamp
        - kind
        - parents
        - hash
    BoundingBox:
      description: |-
        Axis-aligned rectangle, given by its minimum and maximum corners.

        The location type for the image and document modalities: where a
        detected entity sits within a rendered page. [`min`] is the top-left
        corner and [`max`] the bottom-right under the usual screen convention
        (y grows downward), though the box itself is agnostic to coordinate
        orientation.

        [`min`]: Self::min
        [`max`]: Self::max
      type: object
      properties:
        max:
          description: Maximum corner (bottom-right, conventionally).
          allOf:
            - $ref: '#/components/schemas/Point'
        min:
          description: Minimum corner (top-left, conventionally).
          allOf:
            - $ref: '#/components/schemas/Point'
      required:
        - min
        - max
    Polygon:
      description: |-
        Closed polygon, given by its ordered vertices.

        A richer location than a [`BoundingBox`] for detections whose extent
        is not rectangular: rotated text, a region traced by a vision model, a
        signature. The boundary is implicitly closed, so the last vertex
        connects back to the first.

        [`BoundingBox`]: super::BoundingBox
      type: array
      items:
        $ref: '#/components/schemas/Point'
    AudioAuditEvent:
      description: >-
        One node in an entity's audit DAG: a thing that happened, with its

        effect on confidence and its tamper-evident links.


        Events are recorded on an entity's [`AuditLog`], forming the full audit

        trail of its life: each recognizer that found it, the deduplication that

        fused them, any score calibration, and the redaction that hid it. The

        uniform spine (who, resulting score, when) is the same for every event;
        the

        [`kind`] carries the event-specific detail *and* the explanation of why
        the

        event happened.


        Each event links to its [`parents`] by their [`hash`]: a birth event (a

        recognizer's first detection) has no parents, a normal step has one, and
        a

        [fusion](crate::entity::audit::AuditLog::record_fusion) has several. Its
        own

        [`hash`] folds the

        payload together with the parents' hashes, so altering any event breaks

        every event downstream of it: [`AuditLog::verify`] walks the DAG and

        reports the first break.


        `entity.confidence` always equals the [`confidence`] of the most recent

        event. The confidence *flowing in* is not stored: it is the parents'

        [`confidence`], recovered from the DAG.


        [`AuditLog`]: crate::entity::audit::AuditLog

        [`AuditLog::record_fusion`]:
        crate::entity::audit::AuditLog::record_fusion

        [`AuditLog::verify`]: crate::entity::audit::AuditLog::verify

        [`kind`]: AuditEvent::kind

        [`parents`]: AuditEvent::parents

        [`hash`]: AuditEvent::hash

        [`confidence`]: AuditEvent::confidence
      type: object
      properties:
        confidence:
          description: >-
            Confidence after this event: the entity's effective confidence once
            it

            has happened.
          allOf:
            - $ref: '#/components/schemas/Confidence'
        hash:
          description: >-
            This event's hash, over its payload and its parents' hashes.
            Assigned by

            [`AuditLog`] when the event is recorded; read it through

            [`hash`](Self::hash). Recomputing it is how the DAG is verified.


            Not publicly settable: only [`AuditLog`] assigns it, so a caller
            cannot

            forge a self-consistent event outside the recording path.


            [`AuditLog`]: crate::entity::audit::AuditLog
          allOf:
            - $ref: '#/components/schemas/AuditHash'
        kind:
          description: Kind of event, with its event-specific detail and its rationale.
          allOf:
            - $ref: '#/components/schemas/AudioAuditKind'
        parents:
          description: >-
            The events this one follows, by their hash. Empty for a birth event,
            one

            entry for a normal step, several for a fusion. Assigned by
            [`AuditLog`]

            when the event is recorded; read it through
            [`parents`](Self::parents).


            Not publicly settable: the links are the tamper-evident structure,
            so

            only [`AuditLog::record`] / [`AuditLog::record_fusion`] assign them.


            [`AuditLog`]: crate::entity::audit::AuditLog

            [`AuditLog::record`]: crate::entity::audit::AuditLog::record

            [`AuditLog::record_fusion`]:
            crate::entity::audit::AuditLog::record_fusion
          type: array
          items:
            $ref: '#/components/schemas/AuditHash'
        source:
          description: >-
            Who produced this event: a recognizer name, a deduplication
            strategy,

            an operator, or whatever acted.
          type: string
        timestamp:
          description: When the event happened (UTC).
          type: string
      required:
        - source
        - confidence
        - timestamp
        - kind
        - parents
        - hash
    TimeSpan:
      description: |-
        Half-open `[start, end)` stream interval, measured in microseconds.

        The coordinate a time-addressed medium (audio, video) uses to locate a
        region: a transcribed segment, a redacted span. Microsecond precision
        is finer than both word-level speech timings and per-sample audio
        resolution, so a span never loses precision being carried as a
        `TimeSpan`; the endpoints are non-negative offsets by construction.

        Half-open like a byte range: `[start, end)`, so two intervals that
        merely touch (`a.end == b.start`) do not [`overlap`].

        [`from_millis`] and [`as_millis`] bridge the millisecond-based APIs
        that surround it (audio durations, provider timings reported in ms).

        [`overlap`]: Self::overlaps
        [`from_millis`]: Self::from_millis
        [`as_millis`]: Self::start_millis
      type: object
      properties:
        end_us:
          description: |-
            Microseconds from the start of the stream where the interval ends
            (exclusive).
          type: integer
          format: uint64
          minimum: 0
        start_us:
          description: Microseconds from the start of the stream where the interval begins.
          type: integer
          format: uint64
          minimum: 0
      required:
        - start_us
        - end_us
    TabularAuditEvent:
      description: >-
        One node in an entity's audit DAG: a thing that happened, with its

        effect on confidence and its tamper-evident links.


        Events are recorded on an entity's [`AuditLog`], forming the full audit

        trail of its life: each recognizer that found it, the deduplication that

        fused them, any score calibration, and the redaction that hid it. The

        uniform spine (who, resulting score, when) is the same for every event;
        the

        [`kind`] carries the event-specific detail *and* the explanation of why
        the

        event happened.


        Each event links to its [`parents`] by their [`hash`]: a birth event (a

        recognizer's first detection) has no parents, a normal step has one, and
        a

        [fusion](crate::entity::audit::AuditLog::record_fusion) has several. Its
        own

        [`hash`] folds the

        payload together with the parents' hashes, so altering any event breaks

        every event downstream of it: [`AuditLog::verify`] walks the DAG and

        reports the first break.


        `entity.confidence` always equals the [`confidence`] of the most recent

        event. The confidence *flowing in* is not stored: it is the parents'

        [`confidence`], recovered from the DAG.


        [`AuditLog`]: crate::entity::audit::AuditLog

        [`AuditLog::record_fusion`]:
        crate::entity::audit::AuditLog::record_fusion

        [`AuditLog::verify`]: crate::entity::audit::AuditLog::verify

        [`kind`]: AuditEvent::kind

        [`parents`]: AuditEvent::parents

        [`hash`]: AuditEvent::hash

        [`confidence`]: AuditEvent::confidence
      type: object
      properties:
        confidence:
          description: >-
            Confidence after this event: the entity's effective confidence once
            it

            has happened.
          allOf:
            - $ref: '#/components/schemas/Confidence'
        hash:
          description: >-
            This event's hash, over its payload and its parents' hashes.
            Assigned by

            [`AuditLog`] when the event is recorded; read it through

            [`hash`](Self::hash). Recomputing it is how the DAG is verified.


            Not publicly settable: only [`AuditLog`] assigns it, so a caller
            cannot

            forge a self-consistent event outside the recording path.


            [`AuditLog`]: crate::entity::audit::AuditLog
          allOf:
            - $ref: '#/components/schemas/AuditHash'
        kind:
          description: Kind of event, with its event-specific detail and its rationale.
          allOf:
            - $ref: '#/components/schemas/TabularAuditKind'
        parents:
          description: >-
            The events this one follows, by their hash. Empty for a birth event,
            one

            entry for a normal step, several for a fusion. Assigned by
            [`AuditLog`]

            when the event is recorded; read it through
            [`parents`](Self::parents).


            Not publicly settable: the links are the tamper-evident structure,
            so

            only [`AuditLog::record`] / [`AuditLog::record_fusion`] assign them.


            [`AuditLog`]: crate::entity::audit::AuditLog

            [`AuditLog::record`]: crate::entity::audit::AuditLog::record

            [`AuditLog::record_fusion`]:
            crate::entity::audit::AuditLog::record_fusion
          type: array
          items:
            $ref: '#/components/schemas/AuditHash'
        source:
          description: >-
            Who produced this event: a recognizer name, a deduplication
            strategy,

            an operator, or whatever acted.
          type: string
        timestamp:
          description: When the event happened (UTC).
          type: string
      required:
        - source
        - confidence
        - timestamp
        - kind
        - parents
        - hash
    TokenCounts:
      description: |-
        The token counts a model reported.

        Each is optional because providers differ in what they return: some give
        only a total, some none at all.
      type: object
      properties:
        input:
          description: Prompt / input tokens.
          type: integer
          format: uint64
          minimum: 0
        output:
          description: Completion / output tokens.
          type: integer
          format: uint64
          minimum: 0
        total:
          description: >-
            Total tokens, may be reported even when the input/output split is
            not.
          type: integer
          format: uint64
          minimum: 0
    AuditHash:
      description: >-
        A 32-byte BLAKE3 digest.


        The tamper-evident link in an [`AuditLog`] DAG: each [`AuditEvent`]
        hashes

        its payload together with its parents' hashes, so altering any event
        breaks

        every event downstream of it. Displays and (with the `serde` feature)

        (de)serializes as lowercase hex.


        The all-zero hash is [`GENESIS`](AuditHash::GENESIS), the link a birth
        event

        (one with no parents) chains from.


        [`AuditLog`]: crate::entity::audit::AuditLog

        [`AuditEvent`]: crate::entity::audit::AuditEvent
      type: string
    TextAuditKind:
      description: >-
        Kind of an [`AuditEvent`], carrying its event-specific detail and the

        rationale for why it happened.


        A thin tagged union: each variant wraps one payload struct (e.g.

        [`Redaction`], [`Selection`], [`Manual`]) that owns that kind's fields,
        its

        docs, and how it folds into the audit hash. Match on a variant to reach
        its

        payload:


        ```

        # use elide_core::entity::audit::AuditKind;

        # use elide_core::modality::text::Text;

        # fn show(kind: &AuditKind<Text>) {

        if let AuditKind::Redaction(redaction) = kind {
            let _ = &redaction.operator;
        }

        # }

        ```


        `#[non_exhaustive]`: new event kinds (verification, annotation, …) can
        be

        added compatibly. The recognition kinds ([`Pattern`], [`Model`]) carry
        the

        matched [`Location`]; the rest carry their own data.


        [`AuditEvent`]: super::AuditEvent

        [`Pattern`]: AuditKind::Pattern

        [`Model`]: AuditKind::Model

        [`Location`]: Modality::Location
      oneOf:
        - description: A pattern or dictionary recognizer matched here.
          type: object
          properties:
            detail:
              $ref: '#/components/schemas/TextPattern'
            kind:
              type: string
              const: pattern
          required:
            - kind
            - detail
        - description: A model / NER recognizer matched here.
          type: object
          properties:
            detail:
              $ref: '#/components/schemas/TextModel'
            kind:
              type: string
              const: model
          required:
            - kind
            - detail
        - description: |-
            A metadata field (EXIF tag, file timestamp, document property) was
            surfaced here as a redaction subject.
          type: object
          properties:
            detail:
              $ref: '#/components/schemas/TextMetadata'
            kind:
              type: string
              const: metadata
          required:
            - kind
            - detail
        - description: Several detections were fused into one entity.
          type: object
          properties:
            detail:
              $ref: '#/components/schemas/Deduplication'
            kind:
              type: string
              const: deduplication
          required:
            - kind
            - detail
        - description: |-
            A competing detection of a different label over the same span was
            resolved against this (winning) entity.
          type: object
          properties:
            detail:
              $ref: '#/components/schemas/Conflict'
            kind:
              type: string
              const: conflict
          required:
            - kind
            - detail
        - description: >-
            A competing detection of a different label over the same span was
            left

            *unresolved*: both entities survive, flagged for a human to settle.
          type: object
          properties:
            detail:
              $ref: '#/components/schemas/Contested'
            kind:
              type: string
              const: contested
          required:
            - kind
            - detail
        - description: The entity's confidence was rescaled by a per-recognizer factor.
          type: object
          properties:
            detail:
              $ref: '#/components/schemas/Calibration'
            kind:
              type: string
              const: calibration
          required:
            - kind
            - detail
        - description: A context keyword near the entity lifted its confidence.
          type: object
          properties:
            detail:
              $ref: '#/components/schemas/TextRefinement'
            kind:
              type: string
              const: refinement
          required:
            - kind
            - detail
        - description: An operator hid the entity.
          type: object
          properties:
            detail:
              $ref: '#/components/schemas/Redaction'
            kind:
              type: string
              const: redaction
          required:
            - kind
            - detail
        - description: |-
            An operator was *picked* to hide the entity, the redaction decision,
            recorded before it is applied so it can be reviewed first.
          type: object
          properties:
            detail:
              $ref: '#/components/schemas/Selection'
            kind:
              type: string
              const: selection
          required:
            - kind
            - detail
        - description: |-
            A human override, outside automatic detection: an entity a reviewer
            added by hand, or a detected one they marked to ignore.
          type: object
          properties:
            detail:
              $ref: '#/components/schemas/TextManual'
            kind:
              type: string
              const: manual
          required:
            - kind
            - detail
    DecodedSpan:
      description: >-
        A decoded byte range and the raw source it decodes from.


        The `range` is a half-open `[start, end)` span in the decoded text
        stream.

        `source` carries the exact raw byte range(s) that span came from for
        codecs

        whose decoded text differs from the source (XML/HTML/DOCX entity
        decoding,

        JSON escapes); it is empty when the source equals the decoded text.
      type: object
      properties:
        range:
          description: Byte range within the (decoded) text content.
          allOf:
            - $ref: '#/components/schemas/Range_of_uint'
        source:
          description: >-
            The exact raw source ranges this decoded span came from. Empty when
            the

            source equals the decoded text.


            Usually one range; a reconciled span that fused several source runs
            (or a

            span crossing an escape) carries several, kept distinct rather than
            merged

            across gaps. Sorted, deduplicated.
          type: array
          items:
            $ref: '#/components/schemas/SourceRef'
      required:
        - range
    SourceSpan:
      description: >-
        A source-only coordinate: raw byte range(s) with no decoded span.


        The counterpart to [`DecodedSpan`] for content the pipeline never
        decoded (a

        reviewer selecting rendered text). A named struct rather than a bare
        `Vec`

        variant so the coordinate serializes as a map, which an internally
        tagged

        enum can inject its `kind` tag into.
      type: object
      properties:
        source:
          description: |-
            The raw byte range(s) the selection sits in. Sorted, deduplicated,
            non-empty by construction.
          type: array
          items:
            $ref: '#/components/schemas/SourceRef'
      required:
        - source
    ImageAuditKind:
      description: >-
        Kind of an [`AuditEvent`], carrying its event-specific detail and the

        rationale for why it happened.


        A thin tagged union: each variant wraps one payload struct (e.g.

        [`Redaction`], [`Selection`], [`Manual`]) that owns that kind's fields,
        its

        docs, and how it folds into the audit hash. Match on a variant to reach
        its

        payload:


        ```

        # use elide_core::entity::audit::AuditKind;

        # use elide_core::modality::text::Text;

        # fn show(kind: &AuditKind<Text>) {

        if let AuditKind::Redaction(redaction) = kind {
            let _ = &redaction.operator;
        }

        # }

        ```


        `#[non_exhaustive]`: new event kinds (verification, annotation, …) can
        be

        added compatibly. The recognition kinds ([`Pattern`], [`Model`]) carry
        the

        matched [`Location`]; the rest carry their own data.


        [`AuditEvent`]: super::AuditEvent

        [`Pattern`]: AuditKind::Pattern

        [`Model`]: AuditKind::Model

        [`Location`]: Modality::Location
      oneOf:
        - description: A pattern or dictionary recognizer matched here.
          type: object
          properties:
            detail:
              $ref: '#/components/schemas/ImagePattern'
            kind:
              type: string
              const: pattern
          required:
            - kind
            - detail
        - description: A model / NER recognizer matched here.
          type: object
          properties:
            detail:
              $ref: '#/components/schemas/ImageModel'
            kind:
              type: string
              const: model
          required:
            - kind
            - detail
        - description: |-
            A metadata field (EXIF tag, file timestamp, document property) was
            surfaced here as a redaction subject.
          type: object
          properties:
            detail:
              $ref: '#/components/schemas/ImageMetadata'
            kind:
              type: string
              const: metadata
          required:
            - kind
            - detail
        - description: Several detections were fused into one entity.
          type: object
          properties:
            detail:
              $ref: '#/components/schemas/Deduplication'
            kind:
              type: string
              const: deduplication
          required:
            - kind
            - detail
        - description: |-
            A competing detection of a different label over the same span was
            resolved against this (winning) entity.
          type: object
          properties:
            detail:
              $ref: '#/components/schemas/Conflict'
            kind:
              type: string
              const: conflict
          required:
            - kind
            - detail
        - description: >-
            A competing detection of a different label over the same span was
            left

            *unresolved*: both entities survive, flagged for a human to settle.
          type: object
          properties:
            detail:
              $ref: '#/components/schemas/Contested'
            kind:
              type: string
              const: contested
          required:
            - kind
            - detail
        - description: The entity's confidence was rescaled by a per-recognizer factor.
          type: object
          properties:
            detail:
              $ref: '#/components/schemas/Calibration'
            kind:
              type: string
              const: calibration
          required:
            - kind
            - detail
        - description: A context keyword near the entity lifted its confidence.
          type: object
          properties:
            detail:
              $ref: '#/components/schemas/ImageRefinement'
            kind:
              type: string
              const: refinement
          required:
            - kind
            - detail
        - description: An operator hid the entity.
          type: object
          properties:
            detail:
              $ref: '#/components/schemas/Redaction'
            kind:
              type: string
              const: redaction
          required:
            - kind
            - detail
        - description: |-
            An operator was *picked* to hide the entity, the redaction decision,
            recorded before it is applied so it can be reviewed first.
          type: object
          properties:
            detail:
              $ref: '#/components/schemas/Selection'
            kind:
              type: string
              const: selection
          required:
            - kind
            - detail
        - description: |-
            A human override, outside automatic detection: an entity a reviewer
            added by hand, or a detected one they marked to ignore.
          type: object
          properties:
            detail:
              $ref: '#/components/schemas/ImageManual'
            kind:
              type: string
              const: manual
          required:
            - kind
            - detail
    Point:
      description: |-
        Point in a 2-D coordinate space.

        The coordinate basis is left to the consumer: pixel coordinates for a
        raster image, normalized `0.0..=1.0` coordinates for a
        resolution-independent region, or page units for a document. The
        model only requires the two scalars.
      type: object
      properties:
        x:
          description: Horizontal coordinate.
          type: number
          format: double
        'y':
          description: Vertical coordinate.
          type: number
          format: double
      required:
        - x
        - 'y'
    AudioAuditKind:
      description: >-
        Kind of an [`AuditEvent`], carrying its event-specific detail and the

        rationale for why it happened.


        A thin tagged union: each variant wraps one payload struct (e.g.

        [`Redaction`], [`Selection`], [`Manual`]) that owns that kind's fields,
        its

        docs, and how it folds into the audit hash. Match on a variant to reach
        its

        payload:


        ```

        # use elide_core::entity::audit::AuditKind;

        # use elide_core::modality::text::Text;

        # fn show(kind: &AuditKind<Text>) {

        if let AuditKind::Redaction(redaction) = kind {
            let _ = &redaction.operator;
        }

        # }

        ```


        `#[non_exhaustive]`: new event kinds (verification, annotation, …) can
        be

        added compatibly. The recognition kinds ([`Pattern`], [`Model`]) carry
        the

        matched [`Location`]; the rest carry their own data.


        [`AuditEvent`]: super::AuditEvent

        [`Pattern`]: AuditKind::Pattern

        [`Model`]: AuditKind::Model

        [`Location`]: Modality::Location
      oneOf:
        - description: A pattern or dictionary recognizer matched here.
          type: object
          properties:
            detail:
              $ref: '#/components/schemas/AudioPattern'
            kind:
              type: string
              const: pattern
          required:
            - kind
            - detail
        - description: A model / NER recognizer matched here.
          type: object
          properties:
            detail:
              $ref: '#/components/schemas/AudioModel'
            kind:
              type: string
              const: model
          required:
            - kind
            - detail
        - description: |-
            A metadata field (EXIF tag, file timestamp, document property) was
            surfaced here as a redaction subject.
          type: object
          properties:
            detail:
              $ref: '#/components/schemas/AudioMetadata'
            kind:
              type: string
              const: metadata
          required:
            - kind
            - detail
        - description: Several detections were fused into one entity.
          type: object
          properties:
            detail:
              $ref: '#/components/schemas/Deduplication'
            kind:
              type: string
              const: deduplication
          required:
            - kind
            - detail
        - description: |-
            A competing detection of a different label over the same span was
            resolved against this (winning) entity.
          type: object
          properties:
            detail:
              $ref: '#/components/schemas/Conflict'
            kind:
              type: string
              const: conflict
          required:
            - kind
            - detail
        - description: >-
            A competing detection of a different label over the same span was
            left

            *unresolved*: both entities survive, flagged for a human to settle.
          type: object
          properties:
            detail:
              $ref: '#/components/schemas/Contested'
            kind:
              type: string
              const: contested
          required:
            - kind
            - detail
        - description: The entity's confidence was rescaled by a per-recognizer factor.
          type: object
          properties:
            detail:
              $ref: '#/components/schemas/Calibration'
            kind:
              type: string
              const: calibration
          required:
            - kind
            - detail
        - description: A context keyword near the entity lifted its confidence.
          type: object
          properties:
            detail:
              $ref: '#/components/schemas/AudioRefinement'
            kind:
              type: string
              const: refinement
          required:
            - kind
            - detail
        - description: An operator hid the entity.
          type: object
          properties:
            detail:
              $ref: '#/components/schemas/Redaction'
            kind:
              type: string
              const: redaction
          required:
            - kind
            - detail
        - description: |-
            An operator was *picked* to hide the entity, the redaction decision,
            recorded before it is applied so it can be reviewed first.
          type: object
          properties:
            detail:
              $ref: '#/components/schemas/Selection'
            kind:
              type: string
              const: selection
          required:
            - kind
            - detail
        - description: |-
            A human override, outside automatic detection: an entity a reviewer
            added by hand, or a detected one they marked to ignore.
          type: object
          properties:
            detail:
              $ref: '#/components/schemas/AudioManual'
            kind:
              type: string
              const: manual
          required:
            - kind
            - detail
    TabularAuditKind:
      description: >-
        Kind of an [`AuditEvent`], carrying its event-specific detail and the

        rationale for why it happened.


        A thin tagged union: each variant wraps one payload struct (e.g.

        [`Redaction`], [`Selection`], [`Manual`]) that owns that kind's fields,
        its

        docs, and how it folds into the audit hash. Match on a variant to reach
        its

        payload:


        ```

        # use elide_core::entity::audit::AuditKind;

        # use elide_core::modality::text::Text;

        # fn show(kind: &AuditKind<Text>) {

        if let AuditKind::Redaction(redaction) = kind {
            let _ = &redaction.operator;
        }

        # }

        ```


        `#[non_exhaustive]`: new event kinds (verification, annotation, …) can
        be

        added compatibly. The recognition kinds ([`Pattern`], [`Model`]) carry
        the

        matched [`Location`]; the rest carry their own data.


        [`AuditEvent`]: super::AuditEvent

        [`Pattern`]: AuditKind::Pattern

        [`Model`]: AuditKind::Model

        [`Location`]: Modality::Location
      oneOf:
        - description: A pattern or dictionary recognizer matched here.
          type: object
          properties:
            detail:
              $ref: '#/components/schemas/TabularPattern'
            kind:
              type: string
              const: pattern
          required:
            - kind
            - detail
        - description: A model / NER recognizer matched here.
          type: object
          properties:
            detail:
              $ref: '#/components/schemas/TabularModel'
            kind:
              type: string
              const: model
          required:
            - kind
            - detail
        - description: |-
            A metadata field (EXIF tag, file timestamp, document property) was
            surfaced here as a redaction subject.
          type: object
          properties:
            detail:
              $ref: '#/components/schemas/TabularMetadata'
            kind:
              type: string
              const: metadata
          required:
            - kind
            - detail
        - description: Several detections were fused into one entity.
          type: object
          properties:
            detail:
              $ref: '#/components/schemas/Deduplication'
            kind:
              type: string
              const: deduplication
          required:
            - kind
            - detail
        - description: |-
            A competing detection of a different label over the same span was
            resolved against this (winning) entity.
          type: object
          properties:
            detail:
              $ref: '#/components/schemas/Conflict'
            kind:
              type: string
              const: conflict
          required:
            - kind
            - detail
        - description: >-
            A competing detection of a different label over the same span was
            left

            *unresolved*: both entities survive, flagged for a human to settle.
          type: object
          properties:
            detail:
              $ref: '#/components/schemas/Contested'
            kind:
              type: string
              const: contested
          required:
            - kind
            - detail
        - description: The entity's confidence was rescaled by a per-recognizer factor.
          type: object
          properties:
            detail:
              $ref: '#/components/schemas/Calibration'
            kind:
              type: string
              const: calibration
          required:
            - kind
            - detail
        - description: A context keyword near the entity lifted its confidence.
          type: object
          properties:
            detail:
              $ref: '#/components/schemas/TabularRefinement'
            kind:
              type: string
              const: refinement
          required:
            - kind
            - detail
        - description: An operator hid the entity.
          type: object
          properties:
            detail:
              $ref: '#/components/schemas/Redaction'
            kind:
              type: string
              const: redaction
          required:
            - kind
            - detail
        - description: |-
            An operator was *picked* to hide the entity, the redaction decision,
            recorded before it is applied so it can be reviewed first.
          type: object
          properties:
            detail:
              $ref: '#/components/schemas/Selection'
            kind:
              type: string
              const: selection
          required:
            - kind
            - detail
        - description: |-
            A human override, outside automatic detection: an entity a reviewer
            added by hand, or a detected one they marked to ignore.
          type: object
          properties:
            detail:
              $ref: '#/components/schemas/TabularManual'
            kind:
              type: string
              const: manual
          required:
            - kind
            - detail
    TextPattern:
      description: |-
        Detail of a pattern/dictionary recognition: a recognizer matched at
        `location`, with the pattern metadata in `pattern`.
      type: object
      properties:
        location:
          description: Where the recognizer matched.
          allOf:
            - $ref: '#/components/schemas/TextLocation'
        pattern:
          description: Pattern metadata (name, regex, validator, contextual flag).
          allOf:
            - $ref: '#/components/schemas/PatternEvent'
      required:
        - location
        - pattern
    TextModel:
      description: >-
        Detail of a model/NER recognition: a model matched at `location`, with
        its

        metadata in `model`.
      type: object
      properties:
        location:
          description: Where the recognizer matched.
          allOf:
            - $ref: '#/components/schemas/TextLocation'
        model:
          description: Model metadata (name, version, contextual flag).
          allOf:
            - $ref: '#/components/schemas/ModelEvent'
      required:
        - location
        - model
    TextMetadata:
      description: >-
        Detail of a metadata-field detection: a document's out-of-band field

        (an EXIF tag, a file timestamp, a document property) was surfaced as a

        redaction subject at `location`, with its source in `metadata`.


        Distinct from [`Pattern`]/[`Model`] because a metadata field is not
        *matched*

        out of free content — it is a named field that is simply present. There
        is

        nothing probabilistic to weigh, so the entity carries it as its own
        event

        kind rather than pretending a pattern fired.
      type: object
      properties:
        location:
          description: The field's location (its key).
          allOf:
            - $ref: '#/components/schemas/TextLocation'
        metadata:
          description: Source metadata (which reader surfaced the field).
          allOf:
            - $ref: '#/components/schemas/MetadataEvent'
      required:
        - location
        - metadata
    Deduplication:
      description: Several detections were fused into one entity.
      type: object
      properties:
        strategy:
          description: Name of the fusion strategy that combined them.
          type: string
      required:
        - strategy
    Conflict:
      description: >-
        A competing detection of a *different* label over the same span was
        resolved

        against this (winning) entity, the loser is recorded, not dropped.
      type: object
      properties:
        competing_confidence:
          description: The loser's confidence at resolution time.
          allOf:
            - $ref: '#/components/schemas/Confidence'
        competing_label:
          description: The label of the detection that lost arbitration.
          allOf:
            - $ref: '#/components/schemas/LabelRef'
        resolved_by:
          description: Name of the conflict policy that chose the winner.
          type: string
      required:
        - competing_label
        - competing_confidence
        - resolved_by
    Contested:
      description: >-
        A competing detection of a different label over the same span was left

        *unresolved*: both entities survive, flagged for a human. Recorded on
        each

        entity of the contested pair, naming the other.
      type: object
      properties:
        competing_confidence:
          description: The competing detection's confidence.
          allOf:
            - $ref: '#/components/schemas/Confidence'
        competing_label:
          description: The label of the competing detection.
          allOf:
            - $ref: '#/components/schemas/LabelRef'
        flagged_by:
          description: Name of the policy that flagged the contest.
          type: string
      required:
        - competing_label
        - competing_confidence
        - flagged_by
    Calibration:
      description: The entity's confidence was rescaled by a per-recognizer factor.
      type: object
      properties:
        factor:
          description: Multiplier applied.
          type: number
          format: double
      required:
        - factor
    TextRefinement:
      description: A context keyword near the entity lifted its confidence.
      type: object
      properties:
        hint:
          $ref: '#/components/schemas/TextHint'
          description: >-
            The located [`Hint`] the keyword fired from, when the match came
            from an

            out-of-band hint (a column header, a key) rather than the in-text
            word

            window. `None` for an in-text-window match.


            [`Hint`]: crate::modality::Hint
        keyword:
          description: Keyword that fired the boost.
          type: string
        location:
          $ref: '#/components/schemas/TextLocation'
          description: >-
            Where the boosting keyword sits in the medium. For a hint match this

            mirrors the hint's own location; for an in-text-window match it is
            the

            keyword resolved through the modality's [`locate`]. `None` when the

            keyword's stream range could not be placed.


            [`locate`]: crate::modality::TextRecognizable::locate
      required:
        - keyword
    Redaction:
      description: An operator hid the entity.
      type: object
      properties:
        attribution:
          $ref: '#/components/schemas/Attribution'
          description: |-
            The author-supplied policy rationale, when the operator carried an
            [`Attribution`]; `None` otherwise.
        key_id:
          description: Identifier of the key needed to reverse it, if reversible.
          type: string
        leak_profile:
          $ref: '#/components/schemas/LeakProfile'
          description: >-
            How much the output leaks about the original, when the operator
            claimed a

            profile; `None` when it made no claim.
        matched_by:
          description: >-
            Which selection rule chose this operator: the automatic "why"
            (matched a

            label, a tag, a predicate, or the fallback).
          allOf:
            - $ref: '#/components/schemas/RuleMatch'
        operator:
          description: Which operator (name + version) ran.
          allOf:
            - $ref: '#/components/schemas/OperatorId'
        span_hash:
          $ref: '#/components/schemas/AuditHash'
          description: >-
            BLAKE3 digest of the original text the operator hid, when the
            redaction

            layer recorded it. Proves *what* was redacted without storing the

            plaintext; `None` when the operator did not capture it.
        span_length:
          description: |-
            Byte length of the original text the operator hid, paired with
            [`span_hash`](Self::span_hash). `None` when not captured.
          type: integer
          format: uint32
          minimum: 0
      required:
        - operator
        - matched_by
    Selection:
      description: >-
        An operator was *picked* to hide the entity, the redaction decision,

        recorded before it is applied so it can be reviewed (and the entity
        edited)

        first. The [`Redaction`] event that follows records the operator
        actually

        run.
      type: object
      properties:
        attribution:
          $ref: '#/components/schemas/Attribution'
          description: >-
            The author-supplied policy rationale, when the matched rule carried
            an

            [`Attribution`]; `None` otherwise.
        matched_by:
          description: >-
            Which selection rule chose this operator: the automatic "why"
            (matched a

            label, a tag, a predicate, or the fallback).
          allOf:
            - $ref: '#/components/schemas/RuleMatch'
        operator:
          description: >-
            Identity (name + version) of the operator picked. Its *config* is
            not

            recorded; it lives in the policy that will run it, so apply
            re-resolves

            the configured operator rather than reading it here.
          allOf:
            - $ref: '#/components/schemas/OperatorId'
      required:
        - operator
        - matched_by
    TextManual:
      description: >-
        A human override, outside automatic detection: an entity a reviewer
        added by

        hand, or a detected one they marked to ignore. Its provenance is a
        person's

        decision, not a recognizer's, so the trail records *why* (an

        [`Attribution`], when supplied). *Who* made the override is the event's

        [`source`], not a payload field.


        [`source`]: super::AuditEvent::source
      type: object
      properties:
        attribution:
          $ref: '#/components/schemas/Attribution'
          description: |-
            The reviewer's rationale, when supplied (e.g. a freeform
            `"false positive"`, or a cited authority). `None` for an unexplained
            override.
        intent:
          description: >-
            Which human decision this records: including a missed entity, or

            suppressing a detected one. This is the authority on whether the
            entity

            is redacted, [`AuditLog::is_suppressed`] reads it, so there is no

            separate flag to keep in sync.


            [`AuditLog::is_suppressed`]:
            crate::entity::audit::AuditLog::is_suppressed
          allOf:
            - $ref: '#/components/schemas/ManualIntent'
        location:
          description: Where the override applies, in modality-native coordinates.
          allOf:
            - $ref: '#/components/schemas/TextLocation'
      required:
        - intent
        - location
    SourceRef:
      description: >-
        A reference back to the original source: a byte range, and, for a
        container

        whose body spans several files, which part that range indexes.


        [`TextLocation`]'s `range` indexes the *decoded* text stream a codec
        hands

        the pipeline (entities resolved, container parts concatenated). A
        `SourceRef`

        is the *exact raw* byte range the decoded range came from, accounting
        for XML

        escapes where `&amp;` (5 raw bytes) decodes to `&` (1). `part` names the

        container file the range is in (`word/header1.xml`) for a multi-file
        body

        like DOCX, and is `None` for a single-file source (XML, HTML). It lets a

        consumer point back at the untouched source bytes.


        [`TextLocation`]: super::TextLocation
      type: object
      properties:
        part:
          description: >-
            The container part the range indexes, for a multi-file body; `None`
            for a

            single-file source.
          type: string
        range:
          description: The raw source byte range.
          allOf:
            - $ref: '#/components/schemas/Range_of_uint'
      required:
        - range
    ImagePattern:
      description: |-
        Detail of a pattern/dictionary recognition: a recognizer matched at
        `location`, with the pattern metadata in `pattern`.
      type: object
      properties:
        location:
          description: Where the recognizer matched.
          allOf:
            - $ref: '#/components/schemas/ImageLocation'
        pattern:
          description: Pattern metadata (name, regex, validator, contextual flag).
          allOf:
            - $ref: '#/components/schemas/PatternEvent'
      required:
        - location
        - pattern
    ImageModel:
      description: >-
        Detail of a model/NER recognition: a model matched at `location`, with
        its

        metadata in `model`.
      type: object
      properties:
        location:
          description: Where the recognizer matched.
          allOf:
            - $ref: '#/components/schemas/ImageLocation'
        model:
          description: Model metadata (name, version, contextual flag).
          allOf:
            - $ref: '#/components/schemas/ModelEvent'
      required:
        - location
        - model
    ImageMetadata:
      description: >-
        Detail of a metadata-field detection: a document's out-of-band field

        (an EXIF tag, a file timestamp, a document property) was surfaced as a

        redaction subject at `location`, with its source in `metadata`.


        Distinct from [`Pattern`]/[`Model`] because a metadata field is not
        *matched*

        out of free content — it is a named field that is simply present. There
        is

        nothing probabilistic to weigh, so the entity carries it as its own
        event

        kind rather than pretending a pattern fired.
      type: object
      properties:
        location:
          description: The field's location (its key).
          allOf:
            - $ref: '#/components/schemas/ImageLocation'
        metadata:
          description: Source metadata (which reader surfaced the field).
          allOf:
            - $ref: '#/components/schemas/MetadataEvent'
      required:
        - location
        - metadata
    ImageRefinement:
      description: A context keyword near the entity lifted its confidence.
      type: object
      properties:
        hint:
          $ref: '#/components/schemas/ImageHint'
          description: >-
            The located [`Hint`] the keyword fired from, when the match came
            from an

            out-of-band hint (a column header, a key) rather than the in-text
            word

            window. `None` for an in-text-window match.


            [`Hint`]: crate::modality::Hint
        keyword:
          description: Keyword that fired the boost.
          type: string
        location:
          $ref: '#/components/schemas/ImageLocation'
          description: >-
            Where the boosting keyword sits in the medium. For a hint match this

            mirrors the hint's own location; for an in-text-window match it is
            the

            keyword resolved through the modality's [`locate`]. `None` when the

            keyword's stream range could not be placed.


            [`locate`]: crate::modality::TextRecognizable::locate
      required:
        - keyword
    ImageManual:
      description: >-
        A human override, outside automatic detection: an entity a reviewer
        added by

        hand, or a detected one they marked to ignore. Its provenance is a
        person's

        decision, not a recognizer's, so the trail records *why* (an

        [`Attribution`], when supplied). *Who* made the override is the event's

        [`source`], not a payload field.


        [`source`]: super::AuditEvent::source
      type: object
      properties:
        attribution:
          $ref: '#/components/schemas/Attribution'
          description: |-
            The reviewer's rationale, when supplied (e.g. a freeform
            `"false positive"`, or a cited authority). `None` for an unexplained
            override.
        intent:
          description: >-
            Which human decision this records: including a missed entity, or

            suppressing a detected one. This is the authority on whether the
            entity

            is redacted, [`AuditLog::is_suppressed`] reads it, so there is no

            separate flag to keep in sync.


            [`AuditLog::is_suppressed`]:
            crate::entity::audit::AuditLog::is_suppressed
          allOf:
            - $ref: '#/components/schemas/ManualIntent'
        location:
          description: Where the override applies, in modality-native coordinates.
          allOf:
            - $ref: '#/components/schemas/ImageLocation'
      required:
        - intent
        - location
    AudioPattern:
      description: |-
        Detail of a pattern/dictionary recognition: a recognizer matched at
        `location`, with the pattern metadata in `pattern`.
      type: object
      properties:
        location:
          description: Where the recognizer matched.
          allOf:
            - $ref: '#/components/schemas/AudioLocation'
        pattern:
          description: Pattern metadata (name, regex, validator, contextual flag).
          allOf:
            - $ref: '#/components/schemas/PatternEvent'
      required:
        - location
        - pattern
    AudioModel:
      description: >-
        Detail of a model/NER recognition: a model matched at `location`, with
        its

        metadata in `model`.
      type: object
      properties:
        location:
          description: Where the recognizer matched.
          allOf:
            - $ref: '#/components/schemas/AudioLocation'
        model:
          description: Model metadata (name, version, contextual flag).
          allOf:
            - $ref: '#/components/schemas/ModelEvent'
      required:
        - location
        - model
    AudioMetadata:
      description: >-
        Detail of a metadata-field detection: a document's out-of-band field

        (an EXIF tag, a file timestamp, a document property) was surfaced as a

        redaction subject at `location`, with its source in `metadata`.


        Distinct from [`Pattern`]/[`Model`] because a metadata field is not
        *matched*

        out of free content — it is a named field that is simply present. There
        is

        nothing probabilistic to weigh, so the entity carries it as its own
        event

        kind rather than pretending a pattern fired.
      type: object
      properties:
        location:
          description: The field's location (its key).
          allOf:
            - $ref: '#/components/schemas/AudioLocation'
        metadata:
          description: Source metadata (which reader surfaced the field).
          allOf:
            - $ref: '#/components/schemas/MetadataEvent'
      required:
        - location
        - metadata
    AudioRefinement:
      description: A context keyword near the entity lifted its confidence.
      type: object
      properties:
        hint:
          $ref: '#/components/schemas/AudioHint'
          description: >-
            The located [`Hint`] the keyword fired from, when the match came
            from an

            out-of-band hint (a column header, a key) rather than the in-text
            word

            window. `None` for an in-text-window match.


            [`Hint`]: crate::modality::Hint
        keyword:
          description: Keyword that fired the boost.
          type: string
        location:
          $ref: '#/components/schemas/AudioLocation'
          description: >-
            Where the boosting keyword sits in the medium. For a hint match this

            mirrors the hint's own location; for an in-text-window match it is
            the

            keyword resolved through the modality's [`locate`]. `None` when the

            keyword's stream range could not be placed.


            [`locate`]: crate::modality::TextRecognizable::locate
      required:
        - keyword
    AudioManual:
      description: >-
        A human override, outside automatic detection: an entity a reviewer
        added by

        hand, or a detected one they marked to ignore. Its provenance is a
        person's

        decision, not a recognizer's, so the trail records *why* (an

        [`Attribution`], when supplied). *Who* made the override is the event's

        [`source`], not a payload field.


        [`source`]: super::AuditEvent::source
      type: object
      properties:
        attribution:
          $ref: '#/components/schemas/Attribution'
          description: |-
            The reviewer's rationale, when supplied (e.g. a freeform
            `"false positive"`, or a cited authority). `None` for an unexplained
            override.
        intent:
          description: >-
            Which human decision this records: including a missed entity, or

            suppressing a detected one. This is the authority on whether the
            entity

            is redacted, [`AuditLog::is_suppressed`] reads it, so there is no

            separate flag to keep in sync.


            [`AuditLog::is_suppressed`]:
            crate::entity::audit::AuditLog::is_suppressed
          allOf:
            - $ref: '#/components/schemas/ManualIntent'
        location:
          description: Where the override applies, in modality-native coordinates.
          allOf:
            - $ref: '#/components/schemas/AudioLocation'
      required:
        - intent
        - location
    TabularPattern:
      description: |-
        Detail of a pattern/dictionary recognition: a recognizer matched at
        `location`, with the pattern metadata in `pattern`.
      type: object
      properties:
        location:
          description: Where the recognizer matched.
          allOf:
            - $ref: '#/components/schemas/TabularLocation'
        pattern:
          description: Pattern metadata (name, regex, validator, contextual flag).
          allOf:
            - $ref: '#/components/schemas/PatternEvent'
      required:
        - location
        - pattern
    TabularModel:
      description: >-
        Detail of a model/NER recognition: a model matched at `location`, with
        its

        metadata in `model`.
      type: object
      properties:
        location:
          description: Where the recognizer matched.
          allOf:
            - $ref: '#/components/schemas/TabularLocation'
        model:
          description: Model metadata (name, version, contextual flag).
          allOf:
            - $ref: '#/components/schemas/ModelEvent'
      required:
        - location
        - model
    TabularMetadata:
      description: >-
        Detail of a metadata-field detection: a document's out-of-band field

        (an EXIF tag, a file timestamp, a document property) was surfaced as a

        redaction subject at `location`, with its source in `metadata`.


        Distinct from [`Pattern`]/[`Model`] because a metadata field is not
        *matched*

        out of free content — it is a named field that is simply present. There
        is

        nothing probabilistic to weigh, so the entity carries it as its own
        event

        kind rather than pretending a pattern fired.
      type: object
      properties:
        location:
          description: The field's location (its key).
          allOf:
            - $ref: '#/components/schemas/TabularLocation'
        metadata:
          description: Source metadata (which reader surfaced the field).
          allOf:
            - $ref: '#/components/schemas/MetadataEvent'
      required:
        - location
        - metadata
    TabularRefinement:
      description: A context keyword near the entity lifted its confidence.
      type: object
      properties:
        hint:
          $ref: '#/components/schemas/TabularHint'
          description: >-
            The located [`Hint`] the keyword fired from, when the match came
            from an

            out-of-band hint (a column header, a key) rather than the in-text
            word

            window. `None` for an in-text-window match.


            [`Hint`]: crate::modality::Hint
        keyword:
          description: Keyword that fired the boost.
          type: string
        location:
          $ref: '#/components/schemas/TabularLocation'
          description: >-
            Where the boosting keyword sits in the medium. For a hint match this

            mirrors the hint's own location; for an in-text-window match it is
            the

            keyword resolved through the modality's [`locate`]. `None` when the

            keyword's stream range could not be placed.


            [`locate`]: crate::modality::TextRecognizable::locate
      required:
        - keyword
    TabularManual:
      description: >-
        A human override, outside automatic detection: an entity a reviewer
        added by

        hand, or a detected one they marked to ignore. Its provenance is a
        person's

        decision, not a recognizer's, so the trail records *why* (an

        [`Attribution`], when supplied). *Who* made the override is the event's

        [`source`], not a payload field.


        [`source`]: super::AuditEvent::source
      type: object
      properties:
        attribution:
          $ref: '#/components/schemas/Attribution'
          description: |-
            The reviewer's rationale, when supplied (e.g. a freeform
            `"false positive"`, or a cited authority). `None` for an unexplained
            override.
        intent:
          description: >-
            Which human decision this records: including a missed entity, or

            suppressing a detected one. This is the authority on whether the
            entity

            is redacted, [`AuditLog::is_suppressed`] reads it, so there is no

            separate flag to keep in sync.


            [`AuditLog::is_suppressed`]:
            crate::entity::audit::AuditLog::is_suppressed
          allOf:
            - $ref: '#/components/schemas/ManualIntent'
        location:
          description: Where the override applies, in modality-native coordinates.
          allOf:
            - $ref: '#/components/schemas/TabularLocation'
      required:
        - intent
        - location
    PatternEvent:
      description: Metadata of a pattern/dictionary recognition, carried by [`Pattern`].
      type: object
      properties:
        contextual:
          description: >-
            Whether contextual analysis (keyword co-occurrence) adjusted the
            score

            for this match.
          type: boolean
        name:
          description: Name of the pattern that matched (e.g. `"ssn"`, `"email"`).
          type: string
        regex:
          description: Literal regex source that matched, when exposed.
          type: string
        validator:
          description: Name of the validator that confirmed the match (e.g. `"luhn"`).
          type: string
      required:
        - name
        - contextual
    ModelEvent:
      description: Metadata of a model/NER recognition, carried by [`Model`].
      type: object
      properties:
        contextual:
          description: Whether contextual analysis adjusted the score for this match.
          type: boolean
        name:
          description: Model name (e.g. `"spacy-en-core-web-lg"`, `"gpt-4"`).
          type: string
        version:
          description: Model version string, when known.
          type: string
      required:
        - name
        - contextual
    MetadataEvent:
      description: Source detail of a metadata-field detection, carried by [`Metadata`].
      type: object
      properties:
        source:
          description: The reader that surfaced the field (e.g. `"exif"`, `"docprops"`).
          type: string
      required:
        - source
    TextHint:
      description: |-
        Located, typed piece of context a recognizer may treat as in-context
        for a nearby value.

        Out-of-band by nature: a hint is *not* a sub-span of the value it
        informs; it lives elsewhere in the source (a table's column header, a
        JSON object key, a log field name). So `location` points at where the
        hint text actually sits, and `data` is the hint text itself. Carrying
        the location (rather than a bare string) lets a confidence boost record
        *which* hint lifted a score and *where* it came from, provenance a
        review consumer can resolve back to the document.

        Mirrors [`Entity`]'s `location` + `data` shape, so the same
        serialization and lifting patterns apply.

        [`Entity`]: crate::entity::Entity
      type: object
      properties:
        data:
          description: The hint text itself (a column header, a field name).
          allOf:
            - $ref: '#/components/schemas/TextData'
        location:
          description: Where the hint text sits in the source (the header cell, the key).
          allOf:
            - $ref: '#/components/schemas/TextLocation'
      required:
        - location
        - data
    Attribution:
      description: >-
        Author-supplied rationale for a redaction: *under what authority* it was
        made.


        Where the matched selection rule answers *which rule fired*, an

        `Attribution` answers *why the policy demanded it*, a compliance clause,
        an

        internal policy, a data-handling rule. A policy author attaches it to a

        selection rule (`Rule::because` in `elide-redaction`); the anonymizer
        records

        it on the entity's [`Redaction`] event so an audit can trace a change
        back to

        the policy that demanded it.


        The rationale takes one of two shapes, by how much structure the author
        has:

        a [`Freeform`](Attribution::Freeform) label or a formal

        [`Cited`](Attribution::Cited) authority. Start one with

        [`Attribution::freeform`] / [`Attribution::cited`], refine it with the

        shape's `with_*` builder, and let it convert into an `Attribution`:


        ```

        # use elide_core::entity::audit::Attribution;

        let attribution: Attribution =
            Attribution::freeform("gdpr-art-17")
                .with_description("right to erasure")
                .into();
        ```


        [`Redaction`]: crate::entity::audit::AuditKind::Redaction
      oneOf:
        - description: >-
            A policy label and optional human description, with no formal
            citation.
          type: object
          properties:
            kind:
              type: string
              const: freeform
          allOf:
            - $ref: '#/components/schemas/FreeformAttribution'
          required:
            - kind
        - description: >-
            A citable authority, the citation within it, and an optional
            rationale.
          type: object
          properties:
            kind:
              type: string
              const: cited
          allOf:
            - $ref: '#/components/schemas/CitedAttribution'
          required:
            - kind
    LeakProfile:
      description: |-
        What a redacted output leaks about the original it replaced.

        Variants are ordered from most-leaky to least-leaky, so `Recoverable
        < Partial < Irrecoverable`. Surfaced through
        [`Operator::leak_profile`] for policy authoring and audit reporting.

        [`Operator::leak_profile`]: crate::redaction::Operator::leak_profile
      oneOf:
        - description: |-
            The original value is recoverable from the output given the right
            metadata (encryption key, token vault, pseudonym map, or the
            candidate entity list against a hash).
          type: string
          const: recoverable
        - description: |-
            The original value is gone, but observable shape leaks: position,
            length, bounding box, cell coordinates, or a known silence on the
            timeline.
          type: string
          const: partial
        - description: No trace of the original value or its shape remains in the output.
          type: string
          const: irrecoverable
    RuleMatch:
      description: >-
        A serializable summary of *which selection rule* bound an operator to an

        entity, the automatic "why" behind a redaction.


        The anonymizer selects an operator by walking an ordered rule list and

        taking the first match. That decision is recorded on the [`Redaction`]

        event as a `RuleMatch` so an audit can see *why* this operator ran

        ("matched label EMAIL_ADDRESS", "carried tag financial", "the
        fallback").


        This is a summary, not the live rule: a predicate rule can't carry its

        closure into provenance, so [`Predicate`] records only that a predicate

        matched, not which one.


        [`Redaction`]: crate::entity::audit::AuditKind::Redaction

        [`Predicate`]: RuleMatch::Predicate
      oneOf:
        - description: Matched an exact label rule.
          type: object
          properties:
            label:
              $ref: '#/components/schemas/LabelRef'
          additionalProperties: false
          required:
            - label
        - description: 'Matched a tag rule: the entity''s label carries this tag.'
          type: object
          properties:
            tag:
              type: string
          additionalProperties: false
          required:
            - tag
        - description: Matched an arbitrary predicate rule (the closure is not captured).
          type: string
          const: predicate
        - description: Matched the catch-all fallback.
          type: string
          const: fallback
    OperatorId:
      description: |-
        Identifies a redaction operator, for the redaction audit a higher
        layer assembles.

        As with [`RecognizerId`], the version is part of the identity so the
        audit trail records which build of the operator ran. The version is
        opaque text; the core attaches no ordering semantics to it.

        [`RecognizerId`]: crate::recognition::RecognizerId
      type: object
      properties:
        name:
          description: Stable operator name (e.g. `"mask"`, `"aes-gcm-encrypt"`).
          type: string
        version:
          description: Operator's version at the time it was applied.
          type: string
      required:
        - name
        - version
    ManualIntent:
      description: >-
        Which human decision a [`Manual`] event records, the reviewer actions on
        a

        finding: [`Flag`](ManualIntent::Flag) a miss,
        [`Suppress`](ManualIntent::Suppress)

        a false positive, or [`Amend`](ManualIntent::Amend) an existing finding.
      oneOf:
        - description: >-
            A reviewer flagged an entity detection missed (a false negative).
            The

            entity is redacted like any detected one.
          type: string
          const: flag
        - description: >-
            A reviewer marked a detected entity to leave alone (a false
            positive).

            The redaction pass skips it, see [`AuditLog::is_suppressed`].


            [`AuditLog::is_suppressed`]:
            crate::entity::audit::AuditLog::is_suppressed
          type: string
          const: suppress
        - description: >-
            A reviewer amended an existing entity, retagged its label, adjusted
            its

            span, or changed another attribute. The entity stays and is redacted

            normally; this records that a human changed it.
          type: string
          const: amend
    ImageHint:
      description: |-
        Located, typed piece of context a recognizer may treat as in-context
        for a nearby value.

        Out-of-band by nature: a hint is *not* a sub-span of the value it
        informs; it lives elsewhere in the source (a table's column header, a
        JSON object key, a log field name). So `location` points at where the
        hint text actually sits, and `data` is the hint text itself. Carrying
        the location (rather than a bare string) lets a confidence boost record
        *which* hint lifted a score and *where* it came from, provenance a
        review consumer can resolve back to the document.

        Mirrors [`Entity`]'s `location` + `data` shape, so the same
        serialization and lifting patterns apply.

        [`Entity`]: crate::entity::Entity
      type: object
      properties:
        data:
          description: The hint text itself (a column header, a field name).
          allOf:
            - $ref: '#/components/schemas/ImageData'
        location:
          description: Where the hint text sits in the source (the header cell, the key).
          allOf:
            - $ref: '#/components/schemas/ImageLocation'
      required:
        - location
        - data
    AudioHint:
      description: |-
        Located, typed piece of context a recognizer may treat as in-context
        for a nearby value.

        Out-of-band by nature: a hint is *not* a sub-span of the value it
        informs; it lives elsewhere in the source (a table's column header, a
        JSON object key, a log field name). So `location` points at where the
        hint text actually sits, and `data` is the hint text itself. Carrying
        the location (rather than a bare string) lets a confidence boost record
        *which* hint lifted a score and *where* it came from, provenance a
        review consumer can resolve back to the document.

        Mirrors [`Entity`]'s `location` + `data` shape, so the same
        serialization and lifting patterns apply.

        [`Entity`]: crate::entity::Entity
      type: object
      properties:
        data:
          description: The hint text itself (a column header, a field name).
          allOf:
            - $ref: '#/components/schemas/AudioData'
        location:
          description: Where the hint text sits in the source (the header cell, the key).
          allOf:
            - $ref: '#/components/schemas/AudioLocation'
      required:
        - location
        - data
    TabularHint:
      description: |-
        Located, typed piece of context a recognizer may treat as in-context
        for a nearby value.

        Out-of-band by nature: a hint is *not* a sub-span of the value it
        informs; it lives elsewhere in the source (a table's column header, a
        JSON object key, a log field name). So `location` points at where the
        hint text actually sits, and `data` is the hint text itself. Carrying
        the location (rather than a bare string) lets a confidence boost record
        *which* hint lifted a score and *where* it came from, provenance a
        review consumer can resolve back to the document.

        Mirrors [`Entity`]'s `location` + `data` shape, so the same
        serialization and lifting patterns apply.

        [`Entity`]: crate::entity::Entity
      type: object
      properties:
        data:
          description: The hint text itself (a column header, a field name).
          allOf:
            - $ref: '#/components/schemas/TextData'
        location:
          description: Where the hint text sits in the source (the header cell, the key).
          allOf:
            - $ref: '#/components/schemas/TabularLocation'
      required:
        - location
        - data
    TextData:
      description: |-
        Run of text.

        Either the payload a text recognizer inspects, or the value sliced out
        at an entity's location for an operator.

        Held as a [`HipStr`] so short values inline and longer ones share a
        refcounted buffer, making cheap clones when one payload is passed to
        several recognizers.
      type: string
    FreeformAttribution:
      description: |-
        A [`Freeform`](Attribution::Freeform) rationale: a policy label and an
        optional human description, with no formal citation.
      type: object
      properties:
        description:
          description: Human-readable description (e.g. `"right to erasure"`), when given.
          type: string
        name:
          description: The policy's name (e.g. `"gdpr-art-17"`, `"hipaa-safe-harbor"`).
          type: string
      required:
        - name
    CitedAttribution:
      description: >-
        A [`Cited`](Attribution::Cited) rationale: a citable authority, the
        citation

        within it, and an optional rationale for why it applies.
      type: object
      properties:
        authority:
          description: The authority cited (e.g. `"GDPR"`, `"HIPAA"`, `"internal-policy"`).
          type: string
        citation:
          description: >-
            The citation within that authority (e.g. `"Art. 17(1)"`,
            `"§164.514"`).
          type: string
        rationale:
          description: >-
            Why the citation applies here (e.g. `"data subject requested
            erasure"`),

            when given.
          type: string
      required:
        - authority
        - citation
    ImageData:
      description: |-
        Per-call payload a recognizer inspects for the [`Image`] modality.

        Carries the encoded bytes plus the pixel [`Dimensions`], which a
        recognizer that emits unit-square boxes needs to scale them into pixel
        coordinates. An optional filename aids diagnostics and encoding
        inference.

        [`Image`]: super::Image
      type: object
      properties:
        dimensions:
          description: Pixel dimensions of the encoded image.
          allOf:
            - $ref: '#/components/schemas/Dimensions'
        filename:
          description: Original filename, when known.
          type: string
      required:
        - dimensions
    AudioData:
      description: |-
        Per-call payload a recognizer inspects for the [`Audio`] modality.

        Carries the encoded audio bytes; an optional filename aids diagnostics
        and encoding inference (the container format a decoder should expect).
        The recognizable text, a timestamped transcript, is *not* held here;
        a speech-to-text [`Enricher`] stamps it onto the call's
        [`artifact`], keeping
        `AudioData` the codec's payload alone.

        [`Audio`]: super::Audio
        [`Enricher`]: crate::enrichment::Enricher
        [`artifact`]: crate::recognition::RecognizerContext::artifact
      type: object
      properties:
        filename:
          description: Original filename, when known.
          type: string
    Dimensions:
      description: |-
        Pixel dimensions of an image or any 2-D canvas.

        Converts between normalized `0.0..=1.0` coordinates (what vision
        models typically emit) and absolute pixel coordinates (what renderers
        consume). See
        [`UnitBoundingBox::denormalize`]
        for the conversion.

        [`UnitBoundingBox::denormalize`]: super::UnitBoundingBox::denormalize
      type: object
      properties:
        height:
          description: Height in pixels.
          type: integer
          format: uint32
          minimum: 0
        width:
          description: Width in pixels.
          type: integer
          format: uint32
          minimum: 0
      required:
        - width
        - height

````