From drupal-ai-contrib
Runs local verification for Drupal contributions: parity with drupalci jobs (phpcs, phpstan, phpunit, composer), AI-policy gate, and eval gate. Every gate passes only on a captured artifact.
How this skill is triggered — by the user, by Claude, or both
Slash command
/drupal-ai-contrib:contribution-verifyinheritThese tools are removed from Claude's available pool while this skill is active:
The summary Claude sees in its skill listing — used to decide when to auto-load this skill
The fast inner loop. Mirrors the **real drupalci jobs** locally at their **real
The fast inner loop. Mirrors the real drupalci jobs locally at their real strictness, plus the AI-policy and eval gates. Every gate passes only on a produced artifact — a captured command output, a diff, a real result. Never report a bare "passes".
Backs /drupal-ai-contrib:verify. Load the knowledge layer via dev-guides-navigator:
drupal/contributing/drupalci-pipeline-gitlab-templates,
drupal/contributing/drupal-coding-standards-ci-parity,
drupal/contributing/reproducing-drupalci-failures-locally,
drupal/contributing/contrib-project-scaffolding,
drupal/contributing-with-ai/coding-standards,
drupal/contributing-with-ai/testing-ai-code,
drupal/contributing-with-ai/drupal-ai-policy,
drupal/contributing-with-ai/disclosure-checkboxes,
drupal/contributing-with-ai/ai-best-practices-and-evals.
Read from .gitlab-ci.yml: _TARGET_CORE, _TARGET_PHP, _GITLAB_TEMPLATES_REF,
_PHPUNIT_CONCURRENT, SKIP_*, OPT_IN_TEST_*. Install the target core version +
drupal/core-dev so phpunit / phpstan resolve to the releases CI uses. Versions
are resolved, never baked in. If the environment is not matched, say so — local
green on mismatched versions is not evidence.
.gitlab-ci.yml includes the gitlab_templates files. Determine which jobs are
enabled and each job's actual blocking status (allow_failure, SKIP_*). Report
each gate's real blocking status — not a uniform pass/fail.
Run each enabled job locally at CI strictness; capture each one's output as the artifact:
| Gate | How to run at CI strictness |
|---|---|
composer | composer validate + install with the project's constraints |
phpcs | phpcs against the project's phpcs.xml.dist (Drupal + DrupalPractice); drupal/coder ^8.3.x. Blocking by default. |
phpstan | phpstan analyse with the project's phpstan.neon (phpstan-drupal). allow_failure: true by default — report it, flagged non-blocking. |
phpunit | Run with the core config: vendor/bin/phpunit -c web/core/phpunit.xml.dist --webroot=web, switching to core/scripts/run-tests.sh when _PHPUNIT_CONCURRENT: 1. The core phpunit.xml.dist carries failOnWarning / failOnPhpunitWarning — that is what fails on warnings. Pass = zero failures, zero warnings, zero deprecations. |
cspell | cspell with the project's .cspell-project-words.txt loaded |
eslint / stylelint | only when JS/CSS present and not SKIP_ESLINT / SKIP_STYLELINT |
Opt-in variants — OPT_IN_TEST_PREVIOUS_MAJOR / _PREVIOUS_MINOR / _NEXT_MINOR /
_MAX_PHP. gitlab_templates v1.15.0+ moved these to manual trigger — they do not
auto-run. Report opt-in variants explicitly as unrun; never imply coverage. Defer
locally-impossible combinations to contribution-pipeline.
Parity, not philosophy. These gates assert "does the drupalci job pass". Delegate
the philosophy / standards review (SOLID, DRY) to code-quality-tools — parity gates
stay here and are authoritative for "code correctly done".
Runs every time. Dispatch the drupal-ai-contrib:ai-policy-checker agent (Task tool)
to fetch the current state of the adopted Policy on the use of AI when
contributing to Drupal and ai_best_practices — never hard-code policy text. The
gate's pass artifact is:
AI-Generated: Yes (...) disclosure
is prepared.If the ai-policy-checker agent reports a policy source unreachable, the AI-policy
gate is UNRUN — report it as UNRUN, never as passed. The contribution cannot clear
this gate on an unconfirmed policy state.
ai_best_practices ships evals/evals.json (offline grader — PHP lint / phpcs / diff /
security-pattern / report-structure checks). Run the eval set locally as a quality gate
if available; degrade silently if not. Never hard-depend on the eval registry,
never pin its schema, never adopt promptfoo. When guidance fails in practice, capture
an expert correction (correction → fix → eval passes; agent-agnostic JSONL) and offer
to file it upstream.
When 3 or more captured corrections cluster on the same subsystem, surface a recommendation to propose a dedicated skill section or eval suite for it — a recurring failure cluster is a signal the guidance itself has a gap, not just one bad output.
Any path edited after its gate last passed is stale. The PostToolUse
re-verification hook records every edited contribution file in a ledger. Read the stale
set by running ${CLAUDE_PLUGIN_ROOT}/scripts/reverify-list.sh — it prints one path per
line (nothing if no gate is stale). Re-run the gate for every stale path before
reporting; a pre-edit green is not valid. After all gates report green, clear the
ledger: ${CLAUDE_PLUGIN_ROOT}/scripts/reverify-list.sh --clear.
For each gate report: the gate, its actual blocking status, PASS / FAIL / UNRUN,
and the captured artifact (the command output). Never report a verdict without its
artifact. List opt-in variants as UNRUN. State the environment-match status. If a gate
cannot be run locally, say so and defer it to contribution-pipeline — do not imply it
passed.
Trigger: /drupal-ai-contrib:verify
Actions:
composer, phpcs, phpstan, phpunit, cspell; run the AI-policy + eval gates.Trigger: /drupal-ai-contrib:verify after adding a deprecated API call.
Actions:
phpunit runs with the core phpunit.xml.dist (failOnWarning).| Situation | Handling |
|---|---|
No .gitlab-ci.yml found | Report the gap; point at /drupal-ai-contrib:setup for that gap only — do not refuse. |
Local core version ≠ _TARGET_CORE | Environment-match first; a green on mismatched versions is not evidence. |
evals.json absent or schema changed | Degrade silently — the eval gate is best-effort, never a hard dependency. |
A gate cannot run locally (e.g. _MAX_PHP) | Report UNRUN and defer to contribution-pipeline; never imply it passed. |
phpstan reports errors | It is allow_failure: true by default — report FAIL flagged non-blocking, per the project's config. |
verify runs the contributor's own code at CI strictness — composer install,
phpunit, eslint, phpstan — which is untrusted-code execution against an unreviewed
contribution (Composer scripts and a test suite both run arbitrary PHP). When verifying
a contribution you have not yet reviewed, run it under the process-level sandbox runtime
(@anthropic-ai/sandbox-runtime): unlike the built-in Bash sandbox, it wraps the
whole Claude Code process — Bash, file tools, MCP servers, and hooks — in the same
Seatbelt / bubblewrap isolation, so a malicious composer.json script or test fixture
cannot reach beyond the workspace. (The attack surface here is PHPUnit / Composer,
parallel to the npx-trojan risk in JS tooling.) For a fully untrusted repository,
the guide steers stronger isolation — a dedicated VM or Claude Code on the web. See the
Sandbox Environments guide.
For Drupal core or large-suite contributions, scope verify to the contribution's
changed subtree rather than the entire host codebase — point the gates at the paths
the diff actually touches so a multi-gigabyte monorepo does not dilute the signal or
blow the time budget. See the Large Codebases and Monorepos guide.
npx claudepluginhub camoa/claude-skills --plugin drupal-ai-contribEnforces development discipline for AI-assisted Drupal contributions: evidence over assertion, no-guessing rule for external facts, verification-gate artifact contracts, and re-verification on post-gate change.
[ADD v0.9.10] Run quality gates — lint, types, tests, coverage, spec compliance
Enforces verification gate for Drupal implementations using drush evals, curl tests, config imports, and behavioral checks before claiming task completion.