Recording disclosures¶
To record AI involvement from code, call ai_disclosure.recorder, which
implements DisclosureRecorderInterface. That interface is the only class a
calling module needs to know.
suggest()¶
suggest() leaves a non-binding suggestion for the editor to Accept or
Dismiss on the entity form. It never changes the field.
\Drupal::service('ai_disclosure.recorder')->suggest(
$entity,
'ai_partly_assisted',
'my_module',
'Three paragraphs were rewritten by the assistant.',
);
The entity must already be saved, and a second call from the same source on the same entity and language replaces the first one.
apply() and applyProfile()¶
apply() and applyProfile() write the disclosure directly, for headless
pipelines and cron. Both refuse to downgrade: the write happens only when the
requested grade's severity is strictly higher than the entity's currently
effective grade. Both set the human review flag to FALSE, because an
automated caller cannot attest that a person reviewed anything.
\Drupal::service('ai_disclosure.recorder')->applyProfile(
$entity,
'machine_translation',
'my_module',
);
From inside hook_entity_presave(), pass $save = FALSE. The recorder then
changes the field values on the entity object and leaves persistence to the
save cycle already running; calling $entity->save() there would recurse:
\Drupal::service('ai_disclosure.recorder')->apply(
$entity,
'ai_translated',
'my_module',
NULL,
FALSE,
);
What a binding write leaves behind¶
A write that actually happens is recorded in the same
ai_disclosure_suggestion table suggest() writes to, as a row with status
accepted carrying the $source and the $note. It is provenance for a
decision already made, not a proposal: the editor's pending callout and the
report's pending-suggestion count both look at pending rows only, and the
note is never shown to a reader. applyProfile() records the grade the
profile resolves to; which profile it was is on the field itself.
Nothing is recorded when the downgrade rule refuses the write, and nothing is recorded when the write happens during an entity's first insert, because the row is keyed by an entity ID that does not exist yet.
Errors never break the caller's save¶
An unknown grade, an unknown profile or a bundle without the field never
breaks the caller's save: they log a warning on the ai_disclosure channel
and return. The one exception is suggest() on an unsaved entity, which
throws \InvalidArgumentException, because there is no ID to key the
suggestion row on.
The $source argument¶
$source is the calling module's machine name, for example ai_automators
or ai_translate. Every recorder call stores it, so the
ai_disclosure_suggestion table is where a site answers "which module said
this, and why". The compliance report does not expose
that column yet - it reports on the field, not on this table - so a source
that invents its own naming spoils the record for whoever queries it.