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: { }</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> foreach ($this->pre_generate_guardrails['plugin_id'] as $pre_generate_guardrail) {<br> $guardrail_entity = AiGuardrail::load($pre_generate_guardrail);<br> $guardrails[] = $guardrail_entity->getGuardrail();<br> }<br> ...<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> parent::calculateDependencies();<br><br> $guardrail_ids = array_merge(<br> $this->pre_generate_guardrails['plugin_id'] ?? [],<br> $this->post_generate_guardrails['plugin_id'] ?? [],<br> );<br><br> foreach (array_unique($guardrail_ids) as $guardrail_id) {<br> $this->addDependency('config', 'ai.ai_guardrail.' . $guardrail_id);<br> }<br><br> 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