Add universal pre-load and post-load hooks to DocumentLoaderManager::loadFromInput()
>>> [!note] Migrated issue <!-- Drupal.org comment --> <!-- Migrated from issue #3585786. --> Reported by: [ahmad khader](https://www.drupal.org/user/3727855) Related to !33 >>> <h3 id="summary-problem-motivation">Problem/Motivation</h3> <p><code>DocumentLoaderManager::loadFromInput()</code> is the single entry point for all document loading &mdash; PDFs, Word files, URLs, APIs, Slack messages, etc. &mdash; yet there is no way for other modules to hook into this pipeline.</p> <p><strong>Example 1 &mdash; Source tracking:</strong> An <code>ai_context</code> module wants to record which file or URL was loaded and attach provenance metadata. Currently it has no hook to intercept the result before it's consumed.</p> <p><strong>Example 2 &mdash; Content transformation:</strong> A module that normalises Markdown headings or strips PII needs to modify loaded content <em>before</em> it reaches the caller. Without a hook it must patch every consumer individually.</p> <p><strong>Example 3 &mdash; Conditional format override:</strong> A site wants all PDF loads from the MDX editor forced to Markdown output, but leaves other processors on the default format. There's no caller-aware hook to make this conditional.</p> <p><strong>Example 4 &mdash; Logging / auditing:</strong> A governance module wants to log every document load with the source, byte count, and caller. Without a post-load hook it can't do this centrally.</p> <h3 id="summary-proposed-resolution">Proposed resolution</h3> <p>Two <code>ModuleHandlerInterface::alter()</code> hooks in <code>loadFromInput()</code>, each receiving a <strong>mutable first parameter</strong> and a <strong>read-only second parameter</strong>. This makes the API self-documenting: what you <em>can</em> change vs. what's there for context.</p> <p>A <code>$caller</code> string parameter is added to both <code>loadFromInput()</code> and <code>loadFromData()</code> so every hook invocation carries context about <em>who</em> triggered the load. Every existing consumer is updated to pass its identifier.</p> <p><code>$caller</code> parameter on <code>loadFromInput()</code> and <code>loadFromData()</code></p> <p>Both methods accept an optional <code>string $caller = 'api'</code> parameter. All existing processors pass their identifier using PHP named arguments:</p> <ul> <li><code>DocumentLoaderMdxForm</code> &rarr; <code>caller: 'mdx_editor'</code></li> <li><code>DocumentLoaderAction</code> (FWA) &rarr; <code>caller: 'fwa'</code></li> <li><code>DocumentLoaderTool</code> (agent tool) &rarr; <code>caller: 'tool'</code></li> <li><code>DocumentLoaderExplorerForm</code> &rarr; <code>caller: 'explorer'</code></li> <li><code>DocumentLoaderCommands</code> (Drush) &rarr; <code>caller: 'drush'</code></li> <li>Default (direct API) &rarr; <code>'api'</code></li> </ul> <h3 id="summary-remaining-tasks">Remaining tasks</h3> <h3 id="summary-ui-changes">User interface changes</h3> <h3 id="summary-api-changes">API changes</h3> <h3 id="summary-data-model-changes">Data model changes</h3>
issue