Skip to content

STD-DRUPAL-003: Bluefly Drupal-Native Architecture & Upstream-First Refactoring Contract

Status: Approved Governing Standard Authority: Thomas P. Scola Jr. — Drupal Architecture Direction Date: 2026-10-04 Bead: hq-zhg4 (coordination); see §0 for the active producer bead this does not override

Naming note: Engineering-Standard/standards/core/STD-DRUPAL-002-module-architecture-decision-matrix.md also carries a STD-DRUPAL-002-shaped filename but does not self-declare that ID in its own content (no Status/Authority header) and covers a different scope (contrib-module adoption matrix, not custom-module decomposition). The real, self-declared STD-DRUPAL-002 is ledger/STD-DRUPAL-002-composition-and-theme-doctrine.md. This contract takes 003. The filename collision on the other file is a separate doc-hygiene defect, not resolved here.


0. EXECUTION PRECEDENCE — READ BEFORE APPLYING §41's PRIORITY ORDER

Active claimed producer work finishes before this contract's priority order starts.

ACTIVE CLAIMED PRODUCER → COMPLETE (CI → WITNESS → merge → consumer update → close bead)
→ THEN apply this contract's global priority order (§41)

Current example: bead mba-3lyul (mcp_registry/mcp_gateway convergence) is already claimed and in progress. Do not abandon it to start ai_agents_orchestra or any other P0 item below. Finish what is claimed first.

One producer at a time. Parallel read-only investigation/audit across multiple producers is fine. Parallel destructive refactoring across multiple producers at once is not — it multiplies blast radius and makes WITNESS verification ambiguous about which change caused which effect.


Purpose

This contract governs bluefly.io and every Bluefly-owned Drupal module, recipe, theme, integration, and package consumed by or designed for the site.

The objective is not merely to make the current code work. The objective is:

USE DRUPAL CORRECTLY.
DELETE PARALLEL FRAMEWORKS.
USE CORE FIRST. USE CONTRIB SECOND. USE CONFIGURATION THIRD. USE RECIPES FOURTH.
USE EXISTING DRUPAL EXTENSION POINTS. CUSTOM PHP LAST.
MAKE BLUEFLY'S CUSTOM CODE SMALLER, MORE GENERIC, MORE UPSTREAMABLE, AND EASIER TO DELETE.

Every custom line of code is a liability until an upstream gap proves it is necessary.


1. Required Implementation Order

DRUPAL CORE → EXISTING CONTRIB → CONTRIB CONFIGURATION → RECIPE / CONFIG ACTION
→ CANVAS / SDC → ECA / FLOWDROP → DRUPAL AI → AI_AGENTS → TOOL API → MCP
→ EXISTING BLUEFLY MODULE → NEW CUSTOM CODE

Custom code may only survive when every earlier layer has been evaluated and rejected with evidence.


2. MCP Ownership Classification (corrected)

Drupal.org's current direction is drupal/mcp_server (MCP Server 2.x, built on Tool API, HTTP/STDIO transport, OAuth-oriented config) as the actual server implementation target. drupal/mcp is transitional/umbrella, not the target — do not route server ownership there.

MCP_SERVER        = drupal/mcp_server   (plugins, transport, OAuth config)
MCP_CLIENT         = mcp_client          (unchanged — client-side responsibility stays separate,
                                           do not blindly swap this to mcp_server)
MCP_TOOL_BRIDGE    = mcp_server's own plugins
MCP_RESOURCES      = mcp_server's own plugins
MCP_PROMPTS        = mcp_server's own plugins
MCP_OAUTH          = mcp_server's own config

Everywhere elsewhere in this contract that says drupal/mcp / mcp_client for server ownership, read it as corrected to drupal/mcp_server. Client-side mentions of mcp_client are unchanged.


3. What Counts as a Violation

Every implementation must be classified against:

IS_THIS_ACTUALLY_DRUPAL?
DOES_CORE_ALREADY_DO_THIS?            DOES_CONTRIB_ALREADY_DO_THIS?
DOES_DRUPAL_AI_ALREADY_OWN_THIS?      DOES_AI_AGENTS_ALREADY_OWN_THIS?
DOES_TOOL_API_ALREADY_OWN_THIS?       DOES_ECA_ALREADY_OWN_THIS?
DOES_FLOWDROP_ALREADY_OWN_THIS?       DOES_MCP_SERVER_ALREADY_OWN_THIS?
DOES_ADVANCEDQUEUE_ALREADY_OWN_THIS?  DOES_MIGRATE_ALREADY_OWN_THIS?
DOES_CANVAS_ALREADY_OWN_THIS?         DOES_VIEWS_ALREADY_OWN_THIS?
DOES_CONFIGURATION_ALREADY_OWN_THIS?  DOES_A_RECIPE_ALREADY_OWN_THIS?
IS_THIS_FUNCTION_IN_THE_WRONG_MODULE?
IS_THIS_GENERIC_ENOUGH_TO_BELONG_UPSTREAM?
IS_THIS_A_ONE_OFF_BLUEFLY_IMPLEMENTATION_OF_A_GENERIC_PROBLEM?
IS_THIS_AN_INTERNAL_DEV_TOOL_INSIDE_A_PRODUCTION_DRUPAL_MODULE?
IS_THIS_CUSTOM_BECAUSE_SOMEBODY_DID_NOT_LOOK_FOR_THE_EXISTING_FRAMEWORK?

If yes, it is remediation work.


4. P0 — ai_agents_orchestra Must Be Dismantled

Repository: blueflyio/agent-platform/drupal/private/ai_agents_orchestra

The module's own README says: "NEVER implement: custom vector stores, raw Guzzle clients, custom LLM mechanics." Current source does all three — custom agent entities, agent-type entities, orchestration sessions, AI provider plugins, provider management, workflow engines, vector memory, Qdrant integration, dashboards, marketplaces, registries, MCP controllers, execution controllers, policy engine, audit layer, agent routing, framework bridges, CrewAI/ LangChain/OpenAI abstractions, GitLab ML inference, multi-site management, ROI systems, performance agents, queue orchestration, context/memory orchestration — plus direct GuzzleHttp\Client/curl_init()/curl_exec() calls, a hardcoded https://llm-platform.ddev.site, and fake embedding behavior (array_fill, crc32) presented as vector logic. Not acceptable production architecture.

Decomposition targets:

Capability Move to
LLM/model execution drupal/ai — no custom model client, OpenAI/Anthropic router, provider manager/SDK wrapper
Agents drupal/ai_agents — AiAgent plugins, configurable entities, tools, instructions, permissions. Delete custom AIAgent entity, AIAgentType entity, agent-type plugin system, parallel registry/execution loop unless a gap is proven
Memory/context drupal/ai_context, AI Search, Search API, configured vector provider — delete custom VectorMemoryService/QdrantVectorService/framework-specific memory wrappers
Event-driven workflow ECA — no second generic PHP workflow engine
Visual/data workflow FlowDrop — no second graph/workflow format
Async execution AdvancedQueue / Queue API — no parallel scheduler
External automation bridges drupal/orchestration — not a second internal workflow engine
Executable capabilities drupal/tool — no arbitrary custom execution API
Discovery DUADP / OSSA — not Orchestra's job
Policy cedar_policy, contractplane_client, compliance-engine — no second PolicyEngine
Marketplace ai_marketplace, Drupal entities, Views
Dashboards Drupal Dashboard, Views, standard entity list builders — delete AgentDashboardController, EnhancedDashboardController, IntegratedDashboardController, UnifiedDashboardController, UnifiedAgentDashboardController and any other duplicate dashboard interpretation

Config is also slop: parallel naming variants (ai-agent-orchestra.* / ai_agent_orchestra.* / ai_agents_orchestra.*, hyphenated/underscored/pluralized/singular), duplicate ECA models, Views, ECK entity types/bundles, fields, Group types, roles, workflow configs, queue configs under slightly different machine names. Before deleting anything establish ACTIVE_CONFIG / INSTALL_CONFIG / CONSUMERS / UNIQUE_PURPOSE, then converge to one machine name, one config contract, zero duplicate config families.


5. P0 — alternative_services Is the Wrong Layer

Repository: blueflyio/agent-platform/drupal/private/alternative_services

Doing OS/workstation management from inside Drupal: DDEV start/stop/restart/addon install- remove-registry/snapshots/db export-import/logs, Xdebug management, container execution, Composer execution, Drush execution, local process execution, Tailscale Funnel management, SSL operations, service orchestration/routing, external-process management. Drupal should not be the workstation's shell. Move DDEV operations to DDEV addons/commands, host tooling, Gas City execution, agent tooling, CI — not Drupal runtime PHP. Remove production Drupal code that shells out to ddev/composer/drush/tailscale/host processes unless a narrowly scoped, proven administrative integration absolutely requires it.

Submodule collisions, each duplicating existing ownership:

alternative_mcp               → drupal/mcp_server, mcp_tools, Tool API
alternative_router            → drupal/orchestration, ECA, HTTP Client Manager (by operation)
alternative_dragonfly         → dragonfly_client (no second Dragonfly client)
alternative_services_ddev/... → move out of Drupal entirely
alternative_knowledge_graph   → evaluate independently; don't bury a full graph platform here

6. P0 — recipe_onboarding Reimplements Drupal Recipes

Repository: blueflyio/agent-platform/drupal/private/recipe_onboarding

Defines its own Recipe Config Entity, RecipeListBuilder, RecipeForm, RecipeDeleteForm, RecipeApplyForm, recipe status/version/author/tags/application state — while Drupal core already has a Recipe system, and the site already requires drupal/core-recipe-unpack, drupal/recipe_installer_kit, drupal/recipe_ops.

Target: Recipes remain Drupal Recipe artifacts (recipe.yml, config actions, Composer packages). Recipe application uses the core Recipe API / RecipeRunner. Additional UX uses or contributes to Recipe Installer Kit, Recipe Ops, Project Browser. No parallel Recipe entity system. If Bluefly needs recipe inventory/validation/audit/fleet status, those are thin services over the actual Recipe artifacts — the artifact stays the authority.


7. P1 — mcp_registry / mcp_gateway Is Too Large

~120 PHP files, 29 service-path files, 39 plugin-path files, self-described as "MCP server registry, lifecycle management, health monitoring, capability aggregation, AgentDash orchestration." Already depends on drupal/mcp_server-family modules and drupal/tool, drupal/eca, and the module itself says discovery should delegate to contrib — finish that decomposition:

MCP protocol        → drupal/mcp_server
Tool discovery       → Tool API, mcp_tools, tool_explorer
Agent discovery       → DUADP, OSSA, ai_agents
Health                → health_check, monitoring
Automation             → ECA, Orchestration

Legitimate remaining purpose, only if contrib MCP doesn't already own it: MCP server registration metadata, connection configuration, server lifecycle state, site-specific MCP administration. Everything else moves out.

NOTE §0: this module is the subject of the already-claimed, in-progress mba-3lyul bead. Finish that work through CI → WITNESS → merge → consumer update → close before treating this section as new work to start.


8. P1 — ai_agents_client Is Mostly Correct, Still Too Broad

Correctly uses drupal/ai, ai_agents, Tool API, ECA, AdvancedQueue, HTTP Client Manager, MCP, Health Check — keep that direction. Review and narrow: ProtocolRegistry, HTTPAdapter, MCPAdapter, DuoAdapter, DiscoveryService, custom capability discovery, custom protocol registration.

MCP transport                → drupal/mcp_server, mcp_tools (no second MCP transport framework)
HTTP API description/exec    → http_client_manager, api_normalization (no generic custom adapters)
Agent/service discovery       → DUADP, OSSA, ai_agents
GitLab Duo-specific behavior  → split out as ai_agents_client_duo or similar; core client stays
                                 vendor-neutral

9. P1 — ai_agents_communication Should Not Own the Whole A2A World

Contains A2A models/task model/messages/Agent Card model/entity/registry/client, JSON-RPC methods, REST/controller surfaces, Tool plugins, ECA plugins, AiAgent plugin, MCP integration — too many layers in one module. Split generic A2A protocol (a2a_protocol: Agent Card, Message, Task, Task Status, JSON-RPC transport, client/server semantics) from the thin ai_agents_communication integration (A2A ↔ ai_agents, A2A Tool plugins, A2A ECA integration). Discovery does not belong in the communication protocol — use DUADP/OSSA. Fix the duplicate http_client_manager:http_client_manager dependency declaration in .info.yml.


10. P1 — dragonfly_client Has a Good Foundation, Owns Too Much

Strong pattern to keep: HTTP Client Manager, OpenAPI/API description, Tool plugins, ai_agents; README correctly states "HTTP client via http_client_manager, zero custom PHP HTTP client." Move out: agent catalog (→ ai_agents plugin manager, OSSA), capability catalog (→ Tool API plugin manager), discovery manifest/endpoints (→ DUADP/duadp_client/OSSA — this module provides Dragonfly metadata to those systems, it does not implement discovery), agent memory (→ ai_context), vector operations (→ Drupal AI Search/Search API/configured vector provider), generic token/cost telemetry (→ ai_logging/ai_observability/monitoring unless genuinely Dragonfly-specific), rate limiting (→ HTTP Client Manager client policy, or generic Drupal flood service — no invented rate-limit framework).


11. P1 — layout_system_converter Is a Migration Tool, Not a Platform

Contains 28 Tool plugins, AI Agent, FlowDrop nodes, migration jobs, component generation, vector DB registration/health/presets/metrics, WebSocket dependencies — actual purpose is legacy layouts → SDC/Canvas. Keep focused: layout discovery/conversion → Drupal Migrate, Canvas APIs, SDC; AI reasoning → drupal/ai, ai_agents; workflow → FlowDrop/ECA; queue → Drupal Queue/AdvancedQueue. Remove all generic vector-database administration (register/ unregister/health/metrics/presets/config) — unrelated to layout conversion, use the existing AI/vector ecosystem. Fix source metadata: composer.json autoload declares Drupal\canvas_converter\ while current source classes use Drupal\layout_system_converter\ — converge, no alias hacks, no dual namespaces.


12. P1 — external_migration Is Promising but Overbuilt

Already correctly uses Drupal Migrate, Migrate Plus, Migrate Tools, drupal/ai, ai_agents, Tool API, ECA, FlowDrop, Canvas — keep those. Has accumulated a second application platform around them: MigrationGraph, custom orchestrator, custom learning system, performance optimizer, custom enhancement layer, custom pipeline states, custom publish/fidelity gates, custom migration REST API, custom caching/rate limiting — each must be justified independently. Drupal Migrate remains the migration authority (source/process/destination plugins, migration config, messages, rollback, Migrate API/Plus/Tools) — do not recreate Migrate semantics in MigrationGraph unless it only carries Canvas-specific intermediate structure Migrate can't represent. AI field mapping via drupal/ai: keep. AI task decomposition, if agentic: ai_agents, not a custom execution loop. Workflow: ECA/FlowDrop. Publication governance: evaluate Content Moderation/Workflows/ECA before a custom publishing state machine.


13. bluefly.io Consumer Repo — Non-Drupal Control-Plane Slop

Repository: blueflyio/assets/bluefly.io

.agents-workspace/*.mjs, ai.json control_primitives, Node-based governance validators — not Drupal application functionality, useful but wrong ownership layer.

Move generic validation out of the site: workspace-status.mjs, validate-runtime-consumer-install.mjs, validate-metadata-alignment.mjs, validate-control-primitives-schema.mjs, run-validations.mjs → gitlab_components, agent-buildkit, or another canonical shared Bluefly developer tool. One implementation, many consumers.

validate-config-parity is more defensible (delegates to drush config:status rather than reimplementing Drupal config comparison) but is still generic protection that belongs as a shared Drush command / CI component / preflight package, not custom Node wrapper code per-consumer.

validate-blu-chat-config.mjs checks whether YAML files exist and whether a role YAML contains a permission string — this should be a Drupal Kernel/Functional test inside the owning integration module, because Drupal can actually ask "does the role/permission/ assistant config entity/block plugin/AiAgent plugin resolve" — a JS string search cannot. Move it.

ai.json must not become a second Drupal configuration system. It currently drifts from reality (e.g. still describes layout_system = experience_builder while the project uses current Canvas naming; lists custom modules status=active without proof against core.extension.yml). ai.json != RUNTIME AUTHORITY — runtime truth comes from Composer, Drupal extension discovery, active config, core.extension, runtime readback. Reduce ai.json to agent/project metadata or generate its derived sections; do not hand-maintain a second inventory of Drupal.


14. Duplicate Design Systems Must Go

.agents/DesignSystem/ and .agents/DesignSystem-new/ (JSX, HTML prototypes, CSS, tokens, fonts, UI kits, duplicated logo assets, screenshots) may exist as historical design references. They must not remain competing design authorities. Canonical production authority: studio-ui (shared primitives) → bluefly_theme (Drupal SDC implementation) → Canvas (page composition). One design token authority, one component authority, one production theme. Historical prototypes: archive or reference only, never developed further as another frontend.


15. Theme Build Tooling Belongs in the Theme Repo

Consumer package.json currently includes bootstrap, gulp, gulp-cli, gulp-sass, sass — but the consumer declares itself a Drupal runtime consumer, and theme source belongs to bluefly_theme. Theme build tooling moves to bluefly_theme/studio-ui. The site may retain Playwright, Canvas CLI, consumer verification tooling where actually needed.

semantic-release / @semantic-release/* do not belong in this consumer either — release architecture is centralizing in blueflyio/gitlab_components; remove repo-local competing release automation. The project consumes the shared release pipeline, it does not invent its own.


16. Stop Tracking Package Source Under web/

web/ is a Composer/runtime projection. ai.json still allowlists web/themes/custom/bluefly_theme/ as temporarily tracked. Now that bluefly_theme has an owning repository and Composer package, this exception needs a deletion plan. Target: TRACKED_PACKAGE_SOURCE_UNDER_WEB=0. Theme development belongs in the theme repository; the consumer installs the released package.


17. Canvas Should Replace Legacy Layout Authority

canvas, layout_builder, layout_builder_restrictions are all active — not automatically wrong during migration, but target architecture is Canvas. Prove LAYOUT_BUILDER_ACTIVE_CONTENT / LAYOUT_BUILDER_REQUIRED_MODULES / CANVAS_MIGRATION_COMPLETE. When LAYOUT_BUILDER_ACTIVE_CONTENT=0, retire the legacy layout stack. Do not keep two layout systems forever.


18. Contrib Overlap — Pick One Owner Per Concern

Analytics: drupal_cms_google_analytics, ga4_google_analytics, google_analytics, google_tag, gtm, matomo all present. Define PRIMARY_ANALYTICS / TAG_MANAGER / PRIVACY_ANALYTICS, remove unused alternatives.

CAPTCHA/bot protection: captcha, recaptcha, friendlycaptcha, antibot, honeypot. Define FORM_SPAM_BASELINE (honeypot|antibot) and INTERACTIVE_CHALLENGE (friendlycaptcha|recaptcha), remove unused providers.

Email: mailsystem, smtp, sendgrid_integration, symfony_mailer_lite, easy_email, easy_email_theme. Separate mail transport / email template-entity / mail routing, one owner each. Likely clean pattern: Symfony Mailer transport + one provider transport + Easy Email only if its content/template model is actually required — prove current requirements first.

Search: core Search, search_node, Search API, Search API DB, Search API Solr, search_api_exclude. Search API is the canonical abstraction; define PRODUCTION_BACKEND/LOCAL_BACKEND. If Solr is production, Search API Solr owns production indexing; Search API DB may remain only as intentional local/fallback. Core Search/Search Node should not remain a parallel user-facing system without documented use.

Workflow/orchestration: Content Moderation/Workflows = editorial publication state; ECA = event-driven automation; FlowDrop = visual/data pipeline composition; AdvancedQueue = durable async work; Orchestration = external automation bridge; Scheduler = scheduled publish/ unpublish; Ultimate Cron = cron execution control. Maestro and State Machine must prove unique requirements not already covered above. No custom Bluefly module re-implements any of these layers.


19. AI Provider Law

Multiple providers (OpenAI, Anthropic, Hugging Face, Gemini, LiteLLM, Apple) can legitimately coexist, but all model calls go through drupal/ai. No module picks an SDK directly — no Guzzle request to OpenAI, no curl to Anthropic, no custom LLM gateway, no custom provider abstraction. If LiteLLM is the selected production router, configure Drupal AI's LiteLLM provider — do not bypass Drupal AI to reach it. Provider choice is configuration, not application architecture.

Hardcoded model IDs are forbidden (e.g. gpt-3.5-turbo, openai literals inside orchestration logic) — use Drupal AI's default provider / configured operation type / model, unless a feature explicitly requires a particular model and documents why.


20. Agent, Tool, and API Law

Agent law: if a capability is an AI agent, the AiAgent plugin is the implementation boundary. No custom agent runner, ReAct loop, agent state machine, provider-agent abstraction, or "framework bridge" execution engine before proving ai_agents cannot do it.

Tool law: executable capabilities exposed to AI should be Tool plugins. No custom /api/.../execute controller merely to call a PHP service — prefer Tool API → MCP exposure where needed → ai_agents consumption → ECA/FlowDrop integration. One capability, multiple adapters.

API law: before any custom controller route, evaluate JSON:API, Drupal REST, Tool API, drupal/mcp_server, HTTP Client Manager, api_normalization, OpenAPI. Standing site doctrine already says "JSON:API / Tool API / existing packages before bespoke routing" — enforce it.


21. Single-Purpose Functionality Should Move Upstream

Anything not Bluefly-specific, not product-specific, not business-specific, and reusable across Drupal sites must be evaluated for Drupal contrib, an existing upstream project, a generic Bluefly contrib-ready module, a shared GitLab Component, or a shared CLI/tool. Examples from the current estate: config export safety, layout conversion, recipe management UX, MCP registry, A2A protocol, external migration, generic fleet client. These are generic problems — they should not remain permanently private Bluefly glue if genuinely valuable.


22. Function Ownership Map

Functionality Current/likely wrong home Correct owner
Model/provider abstraction Orchestra/custom clients drupal/ai
Agent execution Orchestra/custom loops ai_agents
Agent definition portability custom agent registries OSSA
Agent discovery client/communication/Dragonfly registries DUADP
AI context/memory Orchestra/Dragonfly custom memory ai_context
Vector search custom Qdrant/vector managers AI Search/Search API/provider
Executable AI capability custom controller/service Tool API
MCP server custom adapters/gateway, drupal/mcp drupal/mcp_server
MCP tool discovery custom registry mcp_tools, Tool API
Internal automation custom workflow engine ECA
Visual pipeline custom graphs FlowDrop
Async work custom task executor AdvancedQueue/Queue API
Editorial state custom publish state Content Moderation/Workflows
External workflow bridge custom orchestration client drupal/orchestration
Recipe execution custom Recipe entity Drupal Recipe API
Migration custom migration engine Migrate API
Site composition custom page builder Canvas
Component rendering custom HTML controllers SDC
Lists/dashboards custom list controllers Views/Dashboard
API clients raw Guzzle wrappers HTTP Client Manager/api_normalization
Credentials env strings/custom secrets Key
Health custom health framework Health Check/Monitoring
DDEV operation Drupal module DDEV/host tooling/Gas City
CI/release policy consumer scripts gitlab_components
Generic repository validation site Node scripts shared build/tooling project

23. Custom Code Survival Test

Every Bluefly custom module must produce:

MODULE=                    CUSTOM_PURPOSE=
CORE_ALTERNATIVE=          CONTRIB_ALTERNATIVE=
DRUPAL_AI_ALTERNATIVE=     AI_AGENTS_ALTERNATIVE=
TOOL_ALTERNATIVE=          ECA_ALTERNATIVE=
FLOWDROP_ALTERNATIVE=      MCP_ALTERNATIVE=
WHY_NONE_SUFFICE=
MINIMUM_CUSTOM_SURFACE=
UPSTREAM_CANDIDATE=YES|NO
DELETION_TRIGGER=          OWNER=

If WHY_NONE_SUFFICE cannot be filled with a concrete technical gap: delete/replace the custom code.


24. Module Size Is a Warning Signal

A module whose description requires words like "framework," "platform," "orchestration system," "registry," "marketplace," "memory," "dashboard," "workflow engine," "provider manager," "service mesh" — all in one project — is almost certainly too broad. One Drupal module, one coherent capability boundary. Submodules may provide optional adapters. No mini-platforms inside Drupal.

Prohibited pattern: "Drupal already has X → build another X" for plugin managers, entities/registries, queue, workflow, AI providers, agents, tools, MCP, Migrate. That is architecture slop.


25. Do Not Mass-Delete

This is a refactoring/deletion contract, not permission to blindly remove source. Before any deletion establish CURRENT_CONSUMERS / CONFIG / ACTIVE_RUNTIME / UNIQUE_FUNCTION / TEST_COVERAGE / UPSTREAM_REPLACEMENT / MIGRATION_PATH. Then replace → verify → remove. Never delete → hope.


26. Required Audit of Every Bluefly Drupal Module

MODULE= LINES_OF_CODE= SERVICES= CONTROLLERS= ENTITIES= PLUGIN_MANAGERS=
AI_AGENTS= TOOLS= ECA_PLUGINS= FLOWDROP_NODES= QUEUES= REST_ROUTES=
CUSTOM_HTTP_CLIENTS= CUSTOM_PROCESS_EXECUTION= CUSTOM_STORAGE= CUSTOM_WORKFLOW=
CUSTOM_DISCOVERY= CUSTOM_POLICY= CUSTOM_VECTOR= CUSTOM_AGENT_RUNTIME=

Classify every function: KEEP / MOVE / REPLACE_WITH_CORE / REPLACE_WITH_CONTRIB / REPLACE_WITH_CONFIG / REPLACE_WITH_RECIPE / REPLACE_WITH_ECA / REPLACE_WITH_FLOWDROP / REPLACE_WITH_DRUPAL_AI / REPLACE_WITH_AI_AGENTS / REPLACE_WITH_TOOL / REPLACE_WITH_MCP_SERVER / UPSTREAM / DELETE.


27. Priority Order

Subordinate to §0 (active claimed producer finishes first; one producer at a time).

P0  ai_agents_orchestra
P0  alternative_services
P0  recipe_onboarding

P1  mcp_registry (NOTE: active claimed bead mba-3lyul — finish that, don't restart it)
P1  ai_agents_client
P1  ai_agents_communication
P1  dragonfly_client
P1  layout_system_converter
P1  external_migration

P1  bluefly.io .agents-workspace duplication
P1  ai.json runtime-authority drift
P1  tracked package source under web/

P2  analytics overlap
P2  CAPTCHA overlap
P2  mail overlap
P2  search overlap
P2  workflow/module overlap
P2  design-reference cleanup

Do not start with cosmetic cleanup while the parallel frameworks survive.


28. Target State

CUSTOM_LLM_CLIENTS=0                 CUSTOM_MODEL_ROUTING=0
CUSTOM_AGENT_FRAMEWORKS=0            CUSTOM_GENERIC_WORKFLOW_ENGINES=0
CUSTOM_MCP_TRANSPORTS=0              CUSTOM_RECIPE_ENTITY_SYSTEMS=0
CUSTOM_DDEV_PROCESS_EXECUTION_IN_DRUPAL=0
CUSTOM_GENERIC_VECTOR_MANAGERS=0     HARDCODED_PROVIDER_MODELS=0
RAW_CURL_FOR_AI=0

DUPLICATE_DESIGN_SYSTEM_AUTHORITIES=0
DUPLICATE_RELEASE_SYSTEMS=0

UNJUSTIFIED_CUSTOM_MODULES=0         UNJUSTIFIED_CUSTOM_CONTROLLERS=0
UNJUSTIFIED_CUSTOM_SERVICES=0        UNJUSTIFIED_CONTRIB_OVERLAP=0

DRUPAL_AI_USED_FOR_AI=YES            AI_AGENTS_USED_FOR_AGENTS=YES
TOOL_API_USED_FOR_CAPABILITIES=YES   MCP_SERVER_USED_FOR_MCP=YES
ECA_USED_FOR_EVENT_AUTOMATION=YES    FLOWDROP_USED_FOR_VISUAL_PIPELINES=YES
MIGRATE_USED_FOR_MIGRATION=YES       CANVAS_USED_FOR_COMPOSITION=YES
SDC_USED_FOR_COMPONENTS=YES          VIEWS_USED_FOR_LISTS=YES
RECIPES_USED_FOR_SITE_ASSEMBLY=YES

29. Required Receipt (per module)

MODULE=  BEAD=  CURRENT_PURPOSE=

AUDIT:
  CORE_DUPLICATION=        CONTRIB_DUPLICATION=     BLUEFLY_DUPLICATION=
  WRONG_LAYER=              NON_DRUPAL_FUNCTIONS=    DIRECT_HTTP=
  DIRECT_LLM=                CUSTOM_AGENT_RUNTIME=    CUSTOM_WORKFLOW=
  CUSTOM_VECTOR=              CUSTOM_PROCESS_EXECUTION=

DISPOSITION:
  KEEP=  MOVE=  CONTRIB_REPLACEMENT=  CORE_REPLACEMENT=  CONFIG_REPLACEMENT=
  RECIPE_REPLACEMENT=  ECA_REPLACEMENT=  FLOWDROP_REPLACEMENT=
  DRUPAL_AI_REPLACEMENT=  AI_AGENTS_REPLACEMENT=  TOOL_REPLACEMENT=
  MCP_SERVER_REPLACEMENT=  DELETE=

OWNERSHIP:
  SOURCE_MODULE=  TARGET_MODULES=  UPSTREAM_CANDIDATE=

DELIVERY:
  TESTS=  CONFIG_MIGRATED=  CONSUMERS_MIGRATED=  OLD_CODE_REMOVED=
  MR=  CI=  WITNESS=  MERGED_TO_RELEASE=

CUSTOM_CODE_BEFORE=  CUSTOM_CODE_AFTER=  CUSTOM_CODE_REDUCTION_PERCENT=
UNEXPLAINED_DUPLICATION=0  UNACCOUNTED_FUNCTIONALITY=0

Final Contract

BLUEFLY DOES NOT BUILD A SECOND DRUPAL INSIDE DRUPAL.

CORE OWNS CORE THINGS.                       CONTRIB OWNS GENERIC DRUPAL CAPABILITIES.
DRUPAL AI OWNS MODEL ABSTRACTION.             AI_AGENTS OWNS AGENT EXECUTION.
AI_CONTEXT OWNS AI CONTEXT.                   TOOL API OWNS EXECUTABLE CAPABILITIES.
DRUPAL/MCP_SERVER OWNS MCP.                   ECA OWNS EVENT AUTOMATION.
FLOWDROP OWNS VISUAL PIPELINES.               ADVANCEDQUEUE OWNS DURABLE ASYNC WORK.
MIGRATE OWNS MIGRATION.                       CANVAS OWNS PAGE COMPOSITION.
SDC OWNS COMPONENTS.                          VIEWS OWNS LISTING AND QUERY PRESENTATION.
RECIPES OWN SITE ASSEMBLY.                    KEY OWNS CREDENTIAL REFERENCES.
HTTP CLIENT MANAGER / API NORMALIZATION OWN API CLIENT ABSTRACTION.
GAS CITY OWNS ENGINEERING ORCHESTRATION. DRUPAL DOES NOT MANAGE DDEV, TMUX, TAILSCALE, OR THE
FACTORY.

CUSTOM CODE EXISTS ONLY AFTER A PROVEN GAP.   GENERIC CUSTOM CODE GETS UPSTREAMED.
BLUEFLY-SPECIFIC CODE STAYS THIN.             DUPLICATED FRAMEWORKS GET DELETED.
HACKS GET REDEVELOPED.                        STUBS DO NOT SHIP.
FAKE IMPLEMENTATIONS DO NOT SHIP.             EVERY FUNCTION HAS ONE OWNER.
EVERY MODULE HAS ONE COHERENT PURPOSE.        EVERY CUSTOM LINE HAS A REASON TO EXIST.

IF DRUPAL ALREADY DOES IT, WE USE DRUPAL.

ACTIVE CLAIMED PRODUCER WORK FINISHES BEFORE THIS CONTRACT'S PRIORITY ORDER STARTS.
ONE PRODUCER AT A TIME FOR DESTRUCTIVE REFACTORING.

First three to attack, after any active claimed producer work is finished: ai_agents_orchestra, alternative_services, recipe_onboarding. They have the clearest violations and the largest opportunity to delete custom code rather than refactor it. ai_agents_orchestra in particular is a decomposition project, not a cleanup: preserve the few unique integrations, move them onto the existing Drupal AI/AI Agents/Tool/ECA/FlowDrop stack, then retire the parallel framework.