The Workflow mapping¶
ai_disclosure_workflow connects disclosures to core Workflows and Content
Moderation. Without it, the human review exemption is an honour-system
checkbox: an article can be published in an "AI generated" state and reviewed
by a person a week later, and the disclosure only follows if somebody
remembers to open the node and tick the box.
It ships separately because content_moderation is optional. Plenty of sites
run this module with no editorial workflow at all, and the main module
depends on field and nothing else.
Requirements¶
Core only: content_moderation, which brings workflows with it. The floor
is the same as the main module, PHP 8.1 and Drupal 10.4, so unlike
the Tool plugin there is no site where this submodule is
unavailable while the rest of the module works. If you can enable Content
Moderation, you can enable this.
Where the mapping lives¶
On the workflow itself, at Configuration > Workflow > Workflows, then Edit on the workflow you want. An AI disclosure section sits at the bottom of that page, below the states and the transitions.
The mapping is stored as third-party settings on the workflow config entity,
the same place Content Moderation keeps its own settings. The mapping
therefore exports with drush config:export, travels between environments
with the rest of the workflow, and can be translated where config
translation is in use. A mapped workflow lists this module among its
dependencies, which is how uninstalling gets rid of the mapping: Drupal
strips the settings out of every workflow that carried them. The workflow
itself, its states and its transitions are untouched, but the mapping is
gone and reinstalling does not bring it back.

The section is closed until something is mapped, and opens by itself once something is.
Deleting a state or a transition from the workflow removes its mapping too. Those deletions happen on their own forms, which never see this section, so the module prunes the leftover keys when the workflow is saved.
What you can map¶
Two kinds of mapping, and there is no third.
A state to a profile. Entering that state applies the chosen profile to the entity's disclosure field. Use it where the state itself carries the fact: a state named "AI draft" means what the profile for unreviewed machine output says.
A transition to a profile that asserts human review. Performing that transition attests the review, using the chosen profile. The dropdown for transitions only lists profiles whose "Human review" is on, because a profile without it has nothing to attest and the recorder would refuse it anyway.
If a save matches both a mapped transition and a mapped state, the transition wins. Clicking a transition called "Send to review" says more about what happened than merely landing on the state it leads to.
What it will not do¶
It never invents provenance. Neither kind of mapping can name a grade. Only a profile, and a write that would lower the severity of the current grade is refused: reviewing a machine-written article does not make it human-written, and the exemption the review earns is a separate fact from the grade, which stays true forever. The transition mapping exists for the profile that keeps the same grade and adds the review, so it accepts a write at equal severity.
The editor's own decision comes first. If the disclosure field on the entity being saved is on "custom" or is explicitly suppressed, the mapping is skipped entirely. This covers both the item that already carried an editorial decision and the editor who makes one in the same save as the transition.
It checks that a person could do this. Before writing anything the module
asks Content Moderation whether the current user is allowed to perform that
transition. Setting moderation_state from code and calling save() runs
none of the validation the node form runs, so without this check any script
touching a moderated entity could claim a human review took place. A refusal
is logged on the ai_disclosure_workflow channel and nothing is written.
It stays out of migrations. An entity being synced is replaying a state reached somewhere else. Nobody reviewed anything, so nothing is attested.
It leaves new content alone. A mapping fires on a transition, and an entity being created has no state to come from. The first save of a piece of content therefore keeps whatever disclosure the field gives it.
When a mapping does nothing¶
A mapping can be configured correctly and still write nothing, because the grade never goes down: point a state at a profile whose grade sits below the one the content already carries and the write is refused, every time, and the mapping is dead weight in your config.
That refusal is not silent. The editor saving the content gets a warning
naming the mapping, the profile, and both grades with their severities, so
the reason reads off the message itself, and the same line lands on the
ai_disclosure_workflow log channel with the entity added.

Putting the same entity through the same mapped transition twice is not that case and says nothing: the disclosure is already what the mapping asks for.
An example¶
A newsroom publishing machine-translated wire copy needs two profiles: one
for the translation as it arrives, one for the same translation after a desk
editor has been through it. Both carry the grade ai_translated, because the
translation was machine-made either way. The second has "Human review" on and
names the desk as editorially responsible.
Map the state the imported copy lands in to the first profile, and the transition the desk performs to the second. From then on the disclosure the reader sees changes when the desk does its work, and nobody has to remember a checkbox.
Auditing¶
Moderation state, disclosure field and revision author are all revisioned, so each revision records what was disclosed and who was responsible for the change.
Auto-accepting a matching suggestion¶
A mapped transition can also accept a matching pending suggestion recorded through the recorder API, tying the automated recording and the human review into the same editorial moment. Off by default.
Turn it on at Configuration > Content authoring > AI disclosure > Workflow, one checkbox. It applies only to a mapped transition, never a mapped state: a state is entered, not performed, and has no editorial gesture of its own to attest.
When it fires: the transition attests the human review, and only once that attestation has actually changed the disclosure does the module look for a pending suggestion, on the same entity and in the same language, whose grade matches the grade the entity ends up carrying. A match is accepted. A suggestion proposing a different grade is left pending, untouched: accepting it would fabricate provenance nobody actually attested. If the transition maps to a profile whose grade would downgrade the entity, the attestation is refused (see "When a mapping does nothing" above), and no suggestion is accepted either: nothing was reviewed.