From drupal-core
Core Drupal development rules covering code quality, security, services, and testing verification. ALWAYS consult when writing Drupal code.
npx claudepluginhub ajv009/drupal-devkitThis skill uses the workspace's default tool permissions.
Consolidated rules for Drupal development. Follow these for every implementation task.
Enforces core Drupal 10+ rules for services, dependency injection, security including sanitization and access control, code quality, and testing verification. Always use when writing Drupal code.
Validates Drupal PHP, CSS, and JS against coding standards, SOLID/DRY principles, security practices before commits. Enforces Gate 1 quality gates using checklists and git diff.
Runs code quality audits, security scans, test coverage, SOLID/DRY checks, and lints for Drupal (PHPStan, PHPMD, Psalm, Semgrep, Trivy, Gitleaks) and Next.js (ESLint, Jest, Semgrep, Trivy, Gitleaks) projects.
Share bugs, ideas, or general feedback.
Consolidated rules for Drupal development. Follow these for every implementation task.
\Drupal::* static calls in classes..info.yml, .module, /src (PSR-4), .services.yml.config/install/, not hook_install().<module>.services.yml with interface type-hints.final unless explicitly designed for extension.declare(strict_types=1) in all custom PHP files.private > protected > public.Verify Drupal service changes: after service or Twig changes, confirm service injections are correct, DB table and column names exist, and Twig filters actually exist in the project.
*.services.yml file directly.Drupal::hasService("module_name.service_name") before using any service.group_permission) even when docs or module name suggest plural (group_permissions).Xss::filter() or render arrays.Html::escape() for raw HTML output outside Twig.$entity->access('update') returning ALLOWED does NOT mean the route grants access. Other checkers (archived status, custom gates) may still deny.requirements YAML and trace each _*_access* checker class individually..env, settings.local.php, or credential files.Grep the codebase for remaining references after modifying functions, constants, or variables across files. This catches missed locations that would cause runtime errors.
Search for existing config constants before hardcoding values (e.g., * 8 for hours, * 5 for weekdays). Use system-configured values instead.
When removing or disabling something, remove from ALL locations (controller, template, Twig, JS, config) and grep to confirm nothing was missed.
array_filter(), array_map(), array_reduce() over foreach with nested if/break/continue.accessCheck(TRUE) on entity queries — never omit it.#cache metadata (tags, contexts, max-age) to render arrays.->t() or |t in Twig for TranslatableMarkup.\GuzzleHttp\Utils::jsonDecode/jsonEncode (not PHP's json_*).LoggerInterface methods (debug/info/warning/error) -- no custom debug flags.Drupal\my_module\Service) -- use logical groupings.$snake_case for local variables and function parameters.$lowerCamelCase for class properties/attributes..@Then, @Given, @When).Mandatory gate: Run tests, read output, confirm it proves your claim, THEN claim done. Never say "should work", "looks correct", or "I've implemented X" without test output.
# Status code + response size
ddev exec curl -s -o /dev/null -w "%{http_code} %{size_download}" -b <COOKIE> "http://localhost/<PATH>"
# Download full HTML
ddev exec curl -s -b <COOKIE> "http://localhost/<PATH>" > /tmp/page-output.html
# Get auth cookie
ddev drush uli --uid=1 --no-browser 2>/dev/null
Escaping rules: Use Drupal:: not \Drupal:: in single quotes. Use Exception not \Exception. No use statements. Keep PHP on one line.
ddev drush eval 'print json_encode(["exists" => Drupal::hasService("my.service")]);' 2>/dev/null
Store in scripts/tests/. Update scripts/tests/index.md when creating scripts.
These phrases signal unverified claims -- stop and test first:
ddev exec -- write a script file and execute it.drush uli + curl must be in the same script, same shell, with --uri=http://localhost so the cookie domain matches.edit-features, edit-moderation-state-0) not generic machine names.scripts/tests/ directory with index.chmod +x scripts/tests/*.sh.Naming conventions:
verify-{feature}.sh -- feature verification.debug-{feature}-{aspect}.php -- investigation.check-{aspect}.sh -- quick checks.list-{entities}.php -- data listing.fix-{issue}.php -- one-time fixes.