[Research] Mechanism to force deprecated bundled submodules onto their contrib version (start with ai_validations)
## Summary Research and define a **repeatable mechanism** for forcing a site off a **deprecated bundled submodule** (shipped inside AI Core) and onto the **standalone contrib module** of the same machine name. Use **`ai_validations`** as the first concrete case, then generalise the outcome into a documented "deprecate-and-switch" playbook for the other submodules being moved to contrib. This is the missing *"how"* behind the deprecation work already started in #3552888 (Move out AI Validations), #3554535 / [CR 3570275](https://www.drupal.org/node/3570275) (AI Translate) and #3586497 (AI Search). ## Problem / Motivation `ai_validations` currently exists **twice**: - **Bundled submodule:** `modules/ai_validations/` inside the `ai` project. It is already marked `lifecycle: deprecated` (`lifecycle_link` → #3552888) but has **no `.install` file** — no `hook_requirements()` warning and no migration path. - **Standalone contrib project:** `https://git.drupalcode.org/project/ai_validations` (`drupal/ai_validations`). Both use the **same machine name `ai_validations`**. That is the crux: Drupal cannot have two extensions with the same machine name on the codebase at once, and Composer ships them from two different packages (`drupal/ai` vs `drupal/ai_validations`). So today the deprecation is *advisory only* — nothing actually moves a site across, and there are real failure modes: - A site with the bundled submodule enabled runs `composer update drupal/ai`. When AI Core eventually **removes** the submodule, the module's code disappears from the codebase while it is still enabled and still has config → broken site / missing-module errors, unless `drupal/ai_validations` was separately required first. - Even the existing precedent (AI Translate) only marks the module deprecated and publishes a change record; its `hook_requirements()` and update hooks handle *its own config*, **not** the switch to contrib. So there is no proven, automated switch pattern to copy. We need to research what actually makes the transition safe and, ideally, automatic — so deprecating a bundled submodule does not strand existing sites. ## Research questions 1. **Drupal core precedent.** How has Drupal core handled "module moved to contrib" (e.g. `aggregator`, `rdf`, `color`, `ckeditor`)? What is the supported pattern — `hook_requirements()` errors, change records, composer metapackages, `replace`/`provide` keys, the "moved to contrib" experience? Which parts are reusable here? 2. **Same machine name.** What exactly happens when the bundled submodule code is removed (or replaced) while the contrib module of the same machine name is present? Does enabled state and existing config survive the swap? Verify empirically with a Kernel/Functional test. 3. **Composer strategy.** Can `drupal/ai` express the move so a normal `composer update` pulls the contrib module in automatically — e.g. `conflict`, `require`, `replace`, or a metapackage? What does packages.drupal.org support, and what are the side effects for sites not using Composer (tarball installs)? 4. **Runtime guardrails.** Should the bundled submodule gain a `hook_requirements()` that warns (1.x) and later hard-errors (2.0.x) when it is the bundled copy and a contrib version is available / should be installed? Can it detect which copy is running? 5. **Update hooks.** Can / should an `hook_update_N()` assist (config carry-over, flagging)? Note the hard limit: update hooks **cannot install new code**, so they cannot themselves fetch the contrib module — clarify what they realistically can do. 6. **Version policy & timeline.** What is the supported deprecation window — warn in 1.5.x, remove the bundled submodule in 2.0.x? Align with the 2.0 deprecation policy. ## Approaches to evaluate - **A. Advisory only (status quo):** `lifecycle: deprecated` + change record, remove in next major. Lowest effort; highest risk of broken sites on update; no automation. - **B. Composer-level move:** `drupal/ai` declares a relationship to `drupal/ai_validations` (conflict/require/metapackage) so the contrib package is present before the submodule is removed. Research feasibility and tarball fallout. - **C. Runtime requirements gate:** add `ai_validations.install` with `hook_requirements()` that detects the deprecated bundled copy and instructs the exact switch (warning now, error in 2.0.x), paired with a documented `composer` command. - **D. Combination (likely recommended):** B + C + a change record + a removal milestone — researched and validated on `ai_validations`, then written up as the reusable playbook. ## Deliverables / Scope - A written recommendation (this issue's outcome) for the switch mechanism, with the trade-offs of A–D and a chosen approach. - A proof of concept on **`ai_validations`** (e.g. the `hook_requirements()` gate and/or composer relationship) plus a test proving config + enabled state survive the bundled→contrib swap. - A generalised **"deprecate a bundled submodule → contrib" playbook** other submodules can follow (ai_search per #3586497, and future ones). - A change record, mirroring the AI Translate approach ([3570275](https://www.drupal.org/node/3570275)). Out of scope: actually removing the bundled submodule (that follows in a separate task once the mechanism is agreed), and migrating any specific config beyond what is needed to prove the swap. ## Resources - Bundled submodule: `modules/ai_validations/` (already `lifecycle: deprecated`, no `.install`) - Contrib project: `https://git.drupalcode.org/project/ai_validations` (`drupal/ai_validations`) - Move-out issue: #3552888 · Reference precedent: AI Translate #3554535 + [CR 3570275](https://www.drupal.org/node/3570275) · Next case: AI Search #3586497 - Reference implementation to study: `modules/ai_translate/ai_translate.install` (`hook_requirements()` + update hooks — note these handle config, not the contrib switch) <!-- This issue description was significantly AI-assisted (drafted with Claude Code, reviewed by the creator). -->
issue