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.
Enforces verification gate for Drupal implementations using drush evals, curl tests, config imports, and behavioral checks before claiming task completion.
Enforces strict verification methodology for code quality, requiring actual evidence of working code before shipping. Used with claudikins-kernel:verify and cross-command gates.