Drupal Module Development Guide
Comprehensive guide for developing custom Drupal modules in the LLM Platform ecosystem.
Table of Contents
Overview
The LLM Platform uses custom Drupal modules to provide AI-powered functionality, agent orchestration, and enterprise integrations. All modules follow Drupal coding standards and OSSA compliance requirements.
Key Principles
- OSSA Compliance: All AI agent modules must follow the Open Standard for Swarm Architecture
- Drupal Standards: Follow PHPCS and PHPStan standards (Drupal, DrupalPractice)
- Test Coverage: PHPUnit tests for all critical functionality
- API-First: REST, GraphQL, and JSON:API support
- Security: Input validation, access control, audit logging
Module Structure
Standard Drupal module structure with OSSA enhancements:
module_name/
├── .agents/ # OSSA agent manifests
│ └── agent-name/
│ ├── manifest.json # OSSA manifest
│ ├── README.md
│ └── tools/
├── .claude/ # Claude Code configuration
│ └── CLAUDE.md # Employment protection
├── config/
│ ├── install/ # Default config
│ └── schema/ # Config schema
├── src/
│ ├── Controller/ # Route controllers
│ ├── Entity/ # Entity definitions
│ ├── Form/ # Form classes
│ ├── Plugin/ # Plugin definitions
│ └── Service/ # Services
├── tests/
│ ├── src/
│ │ ├── Unit/ # Unit tests
│ │ ├── Kernel/ # Kernel tests
│ │ └── Functional/ # Functional tests
├── module_name.info.yml # Module metadata
├── module_name.module # Hooks
├── module_name.routing.yml # Routes
├── module_name.services.yml # Service definitions
├── module_name.permissions.yml # Permissions
├── composer.json # Composer dependencies
└── README.md # Documentation
Development Workflow
1. Module Location
Source Location (edit here):
/Users/flux423/Sites/LLM/all_drupal_custom/modules/MODULE_NAME/
Deployment Location (Composer-managed, DO NOT EDIT):
/Users/flux423/Sites/LLM/llm-platform/web/modules/custom/MODULE_NAME/
2. Sync Modules
After making changes, sync to deployment:
buildkit drupal sync --modules
Or with dry-run to preview:
buildkit drupal sync --modules --dry-run
3. Module Creation
Create a new module:
buildkit drupal module:create my_module_name
This scaffolds:
- Basic module structure
- .info.yml with dependencies
- Service definitions
- OSSA compliance files
- CLAUDE.md employment protection
4. Development Commands
# Clear cache
buildkit drupal cache:clear
# Export config
buildkit drupal config:export
# Run database updates
buildkit drupal database:update
# PHPCS validation
buildkit drupal phpcs MODULE_PATH --fix
# Design taste validation
buildkit drupal taste MODULE_PATH
OSSA Compliance
All AI agent modules must be OSSA-compliant.
Manifest Structure
Every agent requires .agents/agent-name/manifest.json:
{
"name": "agent-name",
"version": "1.0.0",
"type": "worker",
"description": "Agent description",
"capabilities": [
"capability1",
"capability2"
],
"tools": [
{
"name": "tool_name",
"type": "function",
"description": "Tool description",
"parameters": {
"type": "object",
"properties": {},
"required": []
}
}
],
"dependencies": [],
"security": {
"isolation_level": "container",
"permissions": []
}
}
Validation
# Validate OSSA manifest
ossa validate .agents/agent-name/manifest.json
# Migrate old manifests
ossa migrate .agents/old-agent/ --output .agents/new-agent/
Best Practices
1. Module Info File
name: 'Module Name'
type: module
description: 'Clear, concise description including OSSA compliance note'
package: 'Package Name'
core_version_requirement: ^10.3 || ^11
version: 0.1.1
dependencies:
# Core dependencies only
- drupal:system (>=10.3)
- drupal:user
suggests:
# Optional enhancements
- admin_toolbar:admin_toolbar
configure: module_name.settings
lifecycle: stable
2. Service Definitions
Use dependency injection:
# module_name.services.yml
services:
module_name.service_name:
class: Drupal\module_name\Service\ServiceName
arguments:
- '@entity_type.manager'
- '@logger.factory'
- '@config.factory'
3. Entity Definitions
Use ECK (Entity Construction Kit) or custom entities:
<?php
namespace Drupal\module_name\Entity;
use Drupal\Core\Entity\ContentEntityBase;
use Drupal\Core\Entity\EntityTypeInterface;
use Drupal\Core\Field\BaseFieldDefinition;
/**
* Defines the Example entity.
*
* @ContentEntityType(
* id = "example_entity",
* label = @Translation("Example Entity"),
* base_table = "example_entity",
* entity_keys = {
* "id" = "id",
* "uuid" = "uuid",
* "label" = "name",
* },
* )
*/
class ExampleEntity extends ContentEntityBase {
public static function baseFieldDefinitions(EntityTypeInterface $entity_type) {
$fields = parent::baseFieldDefinitions($entity_type);
$fields['name'] = BaseFieldDefinition::create('string')
->setLabel(t('Name'))
->setRequired(TRUE);
return $fields;
}
}
4. Plugin System
Use plugins for extensibility:
<?php
namespace Drupal\module_name\Plugin\ActionType;
use Drupal\Core\Plugin\PluginBase;
/**
* @ActionType(
* id = "example_action",
* label = @Translation("Example Action"),
* description = @Translation("Description of action"),
* )
*/
class ExampleAction extends PluginBase {
public function execute(array $context): array {
// Implementation
return ['success' => TRUE];
}
}
5. API Endpoints
Provide REST/GraphQL endpoints:
# module_name.routing.yml
module_name.api.endpoint:
path: '/api/v1/module-name/{id}'
defaults:
_controller: '\Drupal\module_name\Controller\ApiController::getData'
_format: 'json'
requirements:
_permission: 'access module name api'
_method: 'GET'
6. Configuration Schema
Define configuration schema:
# config/schema/module_name.schema.yml
module_name.settings:
type: config_object
label: 'Module Name settings'
mapping:
api_key:
type: string
label: 'API Key'
timeout:
type: integer
label: 'Timeout (seconds)'
Testing
PHPUnit Setup
# Run all tests
vendor/bin/phpunit modules/custom/module_name/tests
# Run specific test suite
vendor/bin/phpunit --testsuite Unit modules/custom/module_name
Unit Test Example
<?php
namespace Drupal\Tests\module_name\Unit;
use Drupal\Tests\UnitTestCase;
use Drupal\module_name\Service\ExampleService;
/**
* @coversDefaultClass \Drupal\module_name\Service\ExampleService
* @group module_name
*/
class ExampleServiceTest extends UnitTestCase {
/**
* @covers ::processData
*/
public function testProcessData() {
$service = new ExampleService();
$result = $service->processData(['input' => 'test']);
$this->assertEquals('expected', $result['output']);
}
}
Kernel Test Example
<?php
namespace Drupal\Tests\module_name\Kernel;
use Drupal\KernelTests\KernelTestBase;
/**
* Tests the Example entity.
*
* @group module_name
*/
class ExampleEntityTest extends KernelTestBase {
protected static $modules = ['module_name', 'user', 'system'];
protected function setUp(): void {
parent::setUp();
$this->installEntitySchema('example_entity');
}
public function testEntityCreation() {
$entity = ExampleEntity::create(['name' => 'Test']);
$entity->save();
$this->assertNotNull($entity->id());
$this->assertEquals('Test', $entity->label());
}
}
Deployment
1. Pre-Deployment Checks
# Run tests
buildkit golden test
# Validate code
buildkit drupal phpcs modules/custom/module_name --fix
# Check design taste
buildkit drupal taste modules/custom/module_name
# Security audit
buildkit golden audit
2. Sync and Deploy
# Sync to deployment location
buildkit drupal sync --modules
# Export configuration
buildkit drupal config:export
# Deploy to environment
buildkit golden deploy --env dev
3. Post-Deployment
# Run database updates
buildkit drupal database:update
# Import configuration
buildkit drupal config:import
# Clear caches
buildkit drupal cache:clear
# Verify module status
drush pm:list --type=module --status=enabled | grep module_name
Employment Protection
All modules include .claude/CLAUDE.md with employment protection:
# CLAUDE.md - module_name
## 🚨 EMPLOYMENT PROTECTION ACTIVE
- Claude Code BLOCKED from development branch
- AI attribution BLOCKED in commits
- Feature branch workflow ENFORCED
## 💰 TOKEN OPTIMIZATION MANDATORY
Before writing ANY code > 500 tokens:
- Check: buildkit agents list
- Use: buildkit agents spawn --type worker
Protected by Agent BuildKit v0.1.9
Common Patterns
AI Integration
// Use AI service
$ai = \Drupal::service('ai.provider.manager');
$provider = $ai->createInstance('openai');
$result = $provider->chat($messages);
ECA Integration
# Workflow automation
langcode: en
status: true
dependencies:
module:
- module_name
id: example_workflow
label: 'Example Workflow'
modeller: bpmn
events:
- id: event_1
plugin: 'content:entity:save'
conditions: []
actions:
- id: action_1
plugin: 'module_name:custom_action'
Queue Processing
// Queue worker
$queue = \Drupal::queue('module_name_tasks');
$queue->createItem(['data' => $data]);
// Process in QueueWorker plugin