Recipe: AI pre-moderation of content via moderation states
>>> [!note] Migrated issue <!-- Drupal.org comment --> <!-- Migrated from issue #3585695. --> Reported by: [marcus_johansson](https://www.drupal.org/user/385947) >>> <p>[Tracker]<br> <strong>Update Summary: </strong>[One-line status update for stakeholders]<br> <strong>Short Description: </strong>Ship a recipe (ai_recipe_content_pre_moderation) that wires AI Automators onto content moderation so non-privileged users create content in a "Reviewable" state and AI automatically moves it to "Flagged" or "Pre-Published".<br> <strong>Check-in Date: </strong>MM/DD/YYYY<br> [/Tracker]</p> <h3 id="summary-problem-motivation">Problem/Motivation</h3> <p>A common real-world need is pre-screening user-generated content before it reaches the public. Community sites, membership platforms, and open-contribution sites all need this: let non-privileged roles create content, but don't publish it until something (or someone) reviews it.</p> <p>The AI module already ships the <code>LlmModerationState</code> automator type (<code>src/Plugin/AiAutomatorType/LlmModerationState.php</code>) which can read content and set a moderation state based on AI evaluation. It supports configurable trigger states, lookup states, and can store the reasoning in a long text field. What's missing is a ready-to-apply recipe that wires this into a real editorial workflow, so site builders get a working pre-moderation pipeline in minutes rather than having to understand and configure each piece manually.</p> <p>See the reference workflow at <a href="https://workflows-of-ai.com/workflow/pre-moderate-content">workflows-of-ai.com/workflow/pre-moderate-content</a> for a video walkthrough of this pattern.</p> <p>The recipe should ship as a separate drupal.org project under the <code>ai_recipe_</code> prefix (suggested name: <code>ai_recipe_content_pre_moderation</code>).</p> <h3 id="summary-proposed-resolution">Proposed resolution</h3> <p>Create a new Drupal recipe project. When applied to a site with the <code>ai</code> module and at least one configured chat provider, the recipe should produce a complete pre-moderation pipeline.</p> <p><strong>Editorial workflow:</strong></p> <ul> <li>Enable the core <code>content_moderation</code> module.</li> <li>Create a new editorial workflow (e.g. "AI Pre-Moderation") with the following states: <strong>Draft</strong> (default, non-published), <strong>Reviewable</strong> (non-published, the trigger state), <strong>Flagged</strong> (non-published, AI determined the content does not meet criteria), <strong>Pre-Published</strong> (non-published, AI determined the content meets criteria and is ready for a human editor to publish), <strong>Published</strong> (published, final human-approved state).</li> <li>Define transitions: Draft &rarr; Reviewable (available to the content-creator role), Reviewable &rarr; Flagged (system/automator only), Reviewable &rarr; Pre-Published (system/automator only), Pre-Published &rarr; Published (editor/admin role), Flagged &rarr; Draft (content-creator can revise and resubmit), Flagged &rarr; Published (editor/admin can override).</li> <li>Apply the workflow to at least the Article content type. Consider making this configurable or documenting how to extend it to other bundles after the recipe is applied.</li> </ul> <p><strong>AI Automator configuration:</strong></p> <ul> <li>The recipe will need to use <a href="https://www.drupal.org/project/base_field_override_ui">base_field_override_ui</a> during recipe authoring to configure the automator on the <code>moderation_state</code> base field via the UI. However, the resulting config exports should apply cleanly without <code>base_field_override_ui</code> as a runtime dependency - document this clearly (the module is needed to build the recipe, not to apply it).</li> <li>Configure the <code>llm_moderation_state</code> automator on the <code>moderation_state</code> field. Set the trigger state to <strong>Reviewable</strong> so the automator only fires when content enters that state. Set the lookup states to <strong>Flagged</strong> and <strong>Pre-Published</strong> so the AI can only choose between those two outcomes.</li> <li>Set the base field to the body field so the AI reads the content being submitted.</li> <li>Craft a prompt that instructs the AI to evaluate whether the content meets editorial standards (no spam, no hate speech, no misinformation, coherent writing, on-topic). If it passes, set Pre-Published; if it fails, set Flagged. The prompt should be shipped as a prompt entity (if prompt entities are available in the target branch) so editors can customise the criteria without editing config.</li> <li>Add a <code>field_moderation_reasoning</code> (long text) field to the content type and configure the automator's "Store explanation" option to write the AI's reasoning into this field, giving editors a one-glance explanation of why content was flagged or pre-published.</li> <li>Use <strong>direct saving</strong> so the moderation state change persists immediately on cron or save.</li> </ul> <p><strong>Permissions and roles:</strong></p> <ul> <li>Create (or document) a "Content Creator" role that can create Article content and transition from Draft &rarr; Reviewable, but cannot transition to Published. This ensures all submissions go through the AI pre-screen.</li> <li>Editors/admins retain the ability to transition from Pre-Published &rarr; Published, and can override a Flagged state by transitioning Flagged &rarr; Published directly when the AI was wrong.</li> </ul> <p><strong>View for moderators:</strong></p> <ul> <li>Ship a View that lists all content in the Flagged and Pre-Published states, showing the moderation reasoning, the author, the submission date, and the current state. Expose filters for state, content type, and date range so editors can triage efficiently.</li> <li>Include a bulk-operations column (via <code>views_bulk_operations</code>) with actions for "Publish", "Send back to Draft", and "Delete" so editors can act from the listing without opening each node.</li> </ul> <p><strong>Open questions:</strong></p> <ul> <li>Should the automator run on entity save (synchronous, immediate state change) or via cron/queue (deferred, no delay on the content-creator's save)? The reference video shows on-save. Document the tradeoff and pick a sensible default; ideally ship a sibling ECA workflow option (see next point).</li> <li>ECA integration: consider shipping an optional ECA workflow that triggers the moderation automator on post-save after the output buffer flushes, so the content creator is not blocked waiting for the AI provider round-trip. This could be a companion submodule or sibling recipe.</li> <li>Which content types to apply the workflow to: just Article, or all content types? Preference: Article only by default, with docs explaining how to extend.</li> <li>Should the recipe also handle image content (e.g. using <code>chat_with_image_vision</code> to screen images in the body)? Probably a follow-up, but worth noting.</li> <li>How to handle the "Reviewable" state on edit: if a user edits already-pre-published content, should it re-enter "Reviewable" for another AI pass? The <code>LlmModerationState</code> automator already supports the "edit when changed" pattern - decide whether to enable it in this recipe.</li> </ul> <h3 id="summary-ai-usage">AI usage (if applicable)</h3> <p>[x] AI Assisted Issue<br> This issue was generated with AI assistance, but was reviewed and refined by the creator.</p> <p>[ ] AI Assisted Code</p> <p>[ ] AI Generated Code</p> <p>[ ] Vibe Coded</p> <p>- <strong>This issue was created with the help of AI</strong></p>
issue