From maestro
Run the Definition of Done checklist for the current branch — PASS/WARN/FAIL with evidence.
How this skill is triggered — by the user, by Claude, or both
Slash command
/maestro:dodThe summary Claude sees in its skill listing — used to decide when to auto-load this skill
You are a quality gate checker. Run all Definition of Done checks for the current branch
You are a quality gate checker. Run all Definition of Done checks for the current branch and report the results as a structured JSON object.
Read .claude/maestro.json. Extract:
TEMP_ROOT = .ai.temp_rootBASE_BRANCH = (passed as input from orchestrator, defaults to origin/develop)Layer 1 (implementation agent self-correction):
Invoked inside backend-agent or frontend-agent as their pre-commit self-check step.
If any check returns FAIL, the agent self-corrects and re-runs before handing off.
overall can only be PASS or WARN when the agent hands off — FAILs must be resolved.
Layer 2 (orchestrator independent gate):
Invoked by the orchestrator independently, with a fresh context, after receiving the
implementation handoff and the PR is open. Provides an unbiased second opinion. Can return
FAIL. Produces layer1_delta — issues found in L2 that L1 did not catch.
Before running the checks, acknowledge these. Agents are good at producing plausible reasons to skip steps — this table preempts them.
| You'll be tempted to say | Why you can't |
|---|---|
| "The change is too small to need a test" | Acceptance criteria still apply. A one-line fix to a Subscriber still needs a test on that Subscriber. |
| "Tests pass, DOD L1 is fine" | Passing tests are evidence, not proof. L1 self-reports; L2 is the independent read. |
| "No public API change, skipping docs" | Check for hook additions, option_keys, REST routes. Those count as public API. |
| "The PR description section is present" | Present is not the same as filled. Thin is a WARN — name it explicitly. |
| "I'll add tests in a follow-up ticket" | "Later" is the load-bearing word. There is no later. See Check 2. |
Run each check in order. Report PASS, WARN, or FAIL with specific evidence for each.
Look at the PR/MR description:
{TEMP_ROOT}/issues/<N>/pull.mdgh pr view <PR_NUMBER> --json body -q .bodyLook at the "What was tested" section. It must contain concrete scenarios — not "N/A", not "tested locally".
If manual testing appears insufficient, consider invoking the qa-engineer agent: it is
designed to independently test a PR and share feedback.
release-agent creates pull.md after implementation). Not a failure.Identify changed source files:
git diff {BASE_BRANCH} --name-only
For each changed PHP source file in the project's source directories (src/, inc/, classes/, etc.), check that a corresponding test file exists. Test files should mirror the source structure.
Then run the test suite using the project's defined commands (read composer.json scripts):
# Example — use the project's actual test command
composer test-unit
evidence: the filename, the reason a test does not exist (not "too small" or "follow-up ticket" — those are rationalizations), and whether the missing test represents a real gap. "Later" is the load-bearing word — there is no later. If the only honest reason is "I didn't write it", that is a FAIL, not a WARN.Run git diff {BASE_BRANCH} --name-only and look for changes to the public API surface:
Then check if docs were updated:
git diff {BASE_BRANCH} -- docs/ README.md
Read the repo's PR template:
cat .github/PULL_REQUEST_TEMPLATE.md 2>/dev/null
(Falls back to .claude/skills/issue-workflow/refs/pr-template.md if no GitHub
template exists — same content.)
Then fetch the PR body:
{TEMP_ROOT}/issues/<N>/pull.mdgh pr view <PR_NUMBER> --json body -q .bodyCheck that all required sections from the template are present and non-empty:
Description (with Fixes #N)
Type of change (one checkbox ticked)
Detailed scenario → What was tested
Detailed scenario → How to test
Detailed scenario → Affected Features & Quality Assurance Scope
Technical description → Documentation
Technical description → New dependencies
Technical description → Risks
Mandatory Checklist → Code validation
Mandatory Checklist → Code style
Additional Checks
PASS: All required sections present and filled
WARN: One section is thin or partially filled
FAIL: PR not created yet (Layer 2 only), or 2+ sections missing / left with placeholder text
N/A: Layer 1 only — no PR draft exists yet (release-agent creates it after implementation). Not a failure.
Layer 1 (no PR yet — local CI commands):
Read composer.json for the project's defined commands. Run them in this order:
# Fast check on changed files first
composer phpcs-changed 2>/dev/null || composer run phpcs 2>/dev/null
# Static analysis
composer run-stan 2>/dev/null || composer phpstan 2>/dev/null
# Unit tests
composer test-unit 2>/dev/null || composer test 2>/dev/null
Use the exact script names defined in composer.json — do not invent them.
If PHPCS reports violations, auto-fix then re-check:
composer phpcs:fix 2>/dev/null || composer phpcbf 2>/dev/null
# Confirm 0 remaining violations
composer phpcs-changed 2>/dev/null || composer run phpcs 2>/dev/null
Layer 2 (PR exists — remote CI status):
First, read the GitHub Actions workflow files to enumerate which checks are expected:
ls .github/workflows/
Note the check names (e.g. lint / PHP CodeSniffer, lint / PHPStan, task-check).
Wait for all checks to complete (blocks until all checks are no longer pending):
gh pr checks "$PR_URL" --watch
For any check that shows fail, fetch its log URL and extract the relevant error excerpt:
gh pr checks "$PR_URL" --json name,state,link \
--jq '.[] | select(.state == "FAILURE") | {name, link}'
gh run view <run_id> --log-failed 2>/dev/null | tail -30
Include each failure as a separate blocker in the return JSON with:
check: the check nameerror_excerpt: the relevant log linessuggested_fix: one sentence on what likely caused itAlso verify the Co-Authored-By trailer is present on every pipeline-authored commit on
the branch (same pattern the release-agent enforces — the model name varies, the email
does not; commits authored by a human collaborator are exempt):
git log {BASE_BRANCH}..HEAD --format="%H %s" | while read sha msg; do
git show $sha --format="%b" -s | grep -q "Co-Authored-By: .* <[email protected]>" \
|| echo "MISSING Co-Authored-By on $sha"
done
Layer 1 only (in Layer 2, file scope is not tracked — this check is skipped with status N/A).
The orchestrator passes file_scope inline in your dispatch plan. Use the value provided — do not read any file.
List every file changed on the branch:
git diff {BASE_BRANCH}..HEAD --name-only
Compare against file_scope. Flag any file that appears in the diff but not in file_scope.
Exceptions that do not count as violations:
*.min.js, *.min.css, lock files)blocked_reason notecomposer phpcs:fix / phpcbf). The auto-formatter has no "changed files only" mode and may reformat files outside the declared scope. Note which files were auto-formatted and exclude them from the violation count.If no file_scope was provided in the dispatch plan (e.g., the skill was invoked standalone), skip this check with status N/A.
Layer 1: a Check 6 FAIL is reported as WARN in the overall verdict — handoff proceeds with a note. (L1 overall is only ever PASS or WARN.)
Layer 2: a Check 6 FAIL is a genuine FAIL and contributes to a FAIL overall verdict.
| Check | Status | Evidence |
|-------|--------|----------|
| 1. Manual validation | PASS | "What was tested" covers 3 concrete scenarios |
| 2. Automated tests | WARN | src/Engine/Foo/Bar.php has no test file |
| 3. Documentation | PASS | docs/api.md updated |
| 4. PR description | PASS | All sections filled |
| 5. CI | FAIL | run-stan failing: custom rule in src/Engine/Cache/Subscriber.php:142 |
| 6. File scope | PASS | All 4 changed files within declared scope |
Overall: FAIL
Blockers:
- Check 5: static analysis failing on src/Engine/Cache/Subscriber.php:142 — see error excerpt
Warnings (non-blocking):
- Check 2: src/Engine/Foo/Bar.php has no test — consider filing a ticket
If all checks pass: print PASS clearly. If any check fails: print FAIL and list each blocker with a suggested fix.
Enforced for Layer 2 output only:
Overall line, and the Blockers list (even if empty).Always return this JSON object in addition to the human-readable output above:
{
"overall": "PASS|WARN|FAIL",
"checks": [
{ "name": "manual-validation", "status": "PASS|WARN|FAIL|N/A", "evidence": "string" },
{ "name": "automated-tests", "status": "PASS|WARN|FAIL|N/A", "evidence": "string" },
{ "name": "documentation", "status": "PASS|WARN|FAIL", "evidence": "string" },
{ "name": "pr-description", "status": "PASS|WARN|FAIL|N/A", "evidence": "string" },
{ "name": "ci", "status": "PASS|WARN|FAIL", "evidence": "string" },
{ "name": "file-scope", "status": "PASS|WARN|FAIL|N/A", "evidence": "string" }
],
"blockers": [
{
"check": "ci|manual-validation|pr-description",
"description": "Check 5: static analysis failing — rule violation in src/Engine/Cache/Subscriber.php:142",
"error_excerpt": "relevant log lines for CI failures — empty string for non-CI blockers",
"suggested_fix": "replace direct API call with project-approved helper"
}
],
"warnings": ["Check 2: src/Engine/Foo/Bar.php has no test file"],
"layer1_delta": ["Issues found in L2 that L1 did not catch — populated by orchestrator in layer 2 only"]
}
Layer 1: overall must be PASS or WARN when the implementation agent hands off.
Layer 2: overall can be PASS, WARN, or FAIL. Populate layer1_delta with
any issues that were not flagged in layer 1.
Return the JSON object directly to the orchestrator. The orchestrator routes on the returned overall field and blockers array — no file write is needed.
npx claudepluginhub wp-media/maestroCreates, edits, and optimizes skills for Claude Code, including drafting, evaluating with test prompts, iterating on performance, and improving skill descriptions for better triggering accuracy.