AiGuardrailSet does not declare config dependencies on its referenced guardrails
>>> [!note] Migrated issue <!-- Drupal.org comment --> <!-- Migrated from issue #3578846. --> Reported by: [abhisekmazumdar](https://www.drupal.org/user/3557964) Related to !1319 >>> <p>[Tracker]<br> <strong>Update Summary: </strong>[One-line status update for stakeholders]<br> <strong>Short Description: </strong>[One-line issue summary for stakeholders]<br> <strong>Check-in Date: </strong>MM/DD/YYYY<br> <em>Metadata is used by the <a href="https://www.drupalstarforge.ai/" title="AI Tracker">AI Tracker.</a> Docs and additional fields <a href="https://www.drupalstarforge.ai/ai-dashboard/docs" title="AI Issue Tracker Documentation">here</a>.</em><br> [/Tracker]</p> <h2>Problem/Motivation</h2> <p><code>AiGuardrailSet</code> extends <code>ConfigEntityBase</code> but does not override <code>calculateDependencies()</code>. The entity stores references to <code>AiGuardrail</code> config entities in its <code>pre_generate_guardrails['plugin_id']</code> and <code>post_generate_guardrails['plugin_id']</code> arrays, but because <code>calculateDependencies()</code> is never called to declare them, Drupal's config system has no knowledge of these relationships.</p> <p>The result is that exported config always shows empty dependencies:</p> <pre><pre>dependencies: {&nbsp; }</pre></pre><p>This has two concrete consequences:</p> <ol> <li><strong>Config import order is not guaranteed.</strong> If a guardrail set and its guardrails are imported together (e.g. via a recipe or a config sync), Drupal may attempt to import the guardrail set before the guardrail entities exist, causing an import failure or silent data loss.</li> <li><strong>Deleting a guardrail does not warn about dependents.</strong> Drupal's config dependency system normally prevents you from deleting a config entity that other entities depend on, or at minimum warns you. Because the dependency is not declared, a guardrail can be deleted while guardrail sets still reference it by ID. At runtime, <code>AiGuardrail::load()</code> will return <code>NULL</code> and <code>getPreGenerateGuardrails()</code> / <code>getPostGenerateGuardrails()</code> will throw a fatal error.</li> </ol> <p>References are loaded at runtime by ID but never declared as dependencies:</p> <pre><pre>public function getPreGenerateGuardrails(): array {<br>&nbsp;&nbsp;&nbsp; foreach ($this-&gt;pre_generate_guardrails['plugin_id'] as $pre_generate_guardrail) {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; $guardrail_entity = AiGuardrail::load($pre_generate_guardrail);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; $guardrails[] = $guardrail_entity-&gt;getGuardrail();<br>&nbsp;&nbsp;&nbsp; }<br>&nbsp;&nbsp;&nbsp; ...<br>}</pre></pre><h2>Proposed resolution</h2> <p>Override <code>calculateDependencies()</code> in <code>AiGuardrailSet</code> to declare each referenced guardrail as a config dependency:</p> <pre><pre>public function calculateDependencies(): static {<br>&nbsp;&nbsp;&nbsp; parent::calculateDependencies();<br><br>&nbsp;&nbsp;&nbsp; $guardrail_ids = array_merge(<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; $this-&gt;pre_generate_guardrails['plugin_id'] ?? [],<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; $this-&gt;post_generate_guardrails['plugin_id'] ?? [],<br>&nbsp;&nbsp;&nbsp; );<br><br>&nbsp;&nbsp;&nbsp; foreach (array_unique($guardrail_ids) as $guardrail_id) {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; $this-&gt;addDependency('config', 'ai.ai_guardrail.' . $guardrail_id);<br>&nbsp;&nbsp;&nbsp; }<br><br>&nbsp;&nbsp;&nbsp; return $this;<br>}</pre></pre><p>With this in place, Drupal will:</p> <ul> <li>Correctly order config imports so guardrails are created before the sets that reference them.</li> <li>Prevent guardrail deletion when a guardrail set still references the guardrail, or cascade-delete the set if the site is configured to do so.</li> </ul> <h2>Remaining tasks</h2> <ul> <li>Add <code>calculateDependencies()</code> to <code>AiGuardrailSet</code></li> <li>Add a test asserting that saving a guardrail set with guardrail references produces the correct config dependencies</li> <li>Verify that existing exported config (e.g. in recipes or distributions) is not broken by re-running config export after the fix</li> </ul> <h2>API changes</h2> <p>None. <code>calculateDependencies()</code> is a protected method override. No public API changes.</p> <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<br> This code was mainly generated by a human, with AI autocompleting or parts AI generated, but under full human supervision.</p> <p>[ ] AI Generated Code<br> This code was mainly generated by an AI with human guidance, and reviewed, tested, and refined by a human.</p> <p>[ ] Vibe Coded<br> This code was generated by an AI and has only been functionally tested.</p>
issue