| name | wp-trac-fix |
| description | Reproduce and attempt a fix for a specific WordPress core defect from Trac in an isolated worktree. Use when the user asks to "work on Trac #N", "fix Trac ticket #N", "reproduce and fix #N", or similar. |
| argument-hint | <ticket-number> |
If no ticket number was provided, ask the user which ticket they want to work on.
wp-trac-fix
Reproduce and fix a WordPress core defect from Trac in an isolated worktree, with discipline that produces evidence-grade outcomes — including NOT-REPRODUCIBLE / INCONCLUSIVE classifications when the bug cannot be triggered.
Preconditions
Verify before starting:
- The current shell is in a clone of
WordPress/wordpress-develop.
- An
upstream remote points at WordPress/wordpress-develop. Confirm with git remote get-url upstream.
- The
envlite tool is on $PATH. Confirm with command -v envlite.
If any precondition fails, stop and report the missing piece. For envlite specifically, tell the user: "envlite was not found on $PATH — install it and ensure the envlite command is reachable, then re-run."
Phase 0 — Setup
Create a per-ticket worktree forked from upstream/trunk and initialize envlite. The slug is a kebab-case summary of the bug (1–4 words, e.g. datepicker-footer-l10n).
git fetch upstream
git worktree add --no-track ../agent-fixes/<ticket> \
-b trac-<ticket>/<slug> upstream/trunk
cd ../agent-fixes/<ticket>
envlite init --force
Note: the ../agent-fixes/<ticket> path is hardcoded relative to the current checkout. If this skill is ever invoked from inside a harness-isolated worktree (e.g. dispatched by a triage-trac-style orchestrator that already provided isolation), skip the git worktree add and operate in place. Revisit this path convention when that integration lands.
envlite init runs npm ci, npm run build:dev, and composer install — minutes on first init. All subsequent work occurs in this worktree. Never cd out of it; never switch branches inside it.
Phase A — Read the ticket
Fetch the ticket via the wp-trac-ticket skill. The default mode returns everything you need — description, attachments, changesets, comments, and any linked pull requests — in a single call.
Read critically — the entire ticket, not only the comments. Descriptions, sample code, and commenter conclusions are all unreliable; they may be wrong, stale, or written without ever being run. Conversely, comments that look dismissable (a patch that didn't apply, a confused author) often contain load-bearing environment details. Read every comment for its evidence, separately from its conclusion.
- "Reproduction report" comments may have tested an adjacent scenario, not the actual claim. Confirm what the ticket claims is broken before accepting any commenter's conclusion. Note meaningful discrepancies in the report's notes field.
- Sample code in the description may not run as written. If a transplanted snippet throws a fatal error, the snippet is broken — that is NOT evidence the underlying bug is absent. Fix the snippet, retry, then decide. See
references/repro-strategies.md for common snippet-failure modes.
- Implicit preconditions are common. A snippet that runs cleanly and produces no symptom may simply be missing the property the bug depends on — specific values, ordering, particular data shapes. Before declaring NOT-REPRODUCIBLE on a silent-but-clean repro, identify what property of the input the bug claim is load-bearing on, and confirm the setup guarantees it. Example: a "dropdown sorts wrong" bug needs entries whose display names sort differently from their underlying values — a setup with one entry, or with names that happen to sort the same as values, shows nothing even though the bug is real.
- Environment blocks in test reports are setup checklists. When a comment includes a full environment (theme, plugins, PHP version) and artifacts (screenshots, screencasts), treat those as primary evidence even if the comment's conclusion is uncertain (e.g. they applied a stale patch). Set up that same environment, fetch the artifacts, and reproduce there before reaching for any synthetic setup (filter-injected templates, custom mu-plugin scaffolding). Synthetic repros are fine for isolating the mechanism, but only after the stock-environment repro is replayed — or shown affirmatively impossible. The stock-environment repro is what a release manager will replay; a filter-injected one proves the mechanism but doesn't substitute.
Phase B — Reproduce
Pick a strategy from the matrix and attempt once before escalating.
| Ticket signal | Strategy |
|---|
| PHP API behavior | phpunit |
| Admin UI / front-end rendering / browser-specific | browser MCP via envlite up |
| Block editor / JS module / JS function correctness | qunit (CLI; open in browser via a browser MCP for visual debugging) |
| REST / Ajax endpoints | phpunit first; escalate to a browser MCP only if PHP harness cannot reach the codepath |
| Multisite / cron / upgrade | phpunit (multisite group / cron tests / upgrade tests) |
| Unclear, "looks wrong", "is slow" | browser MCP |
"Browser MCP" here means any MCP that drives a real browser (Playwright MCP being one such tool). Pick whichever is available in the session.
For browser-driven repro: run envlite up --force backgrounded, read port from .envlite/port, then drive http://127.0.0.1:<port>/ via the browser MCP. Admin login: admin / password.
For phpunit: vendor/bin/phpunit --filter <test_name> from the worktree root.
See references/repro-strategies.md for detailed per-strategy guidance.
Classifications
After Phase B, classify the outcome:
- REPRODUCED+FIXED — reproduced concretely; fix written; tests pass.
- REPRODUCED+UNFIXED — reproduced, but the right fix exceeds the scope cap (see Phase C).
- NOT-REPRODUCIBLE — affirmative evidence the bug does not occur. Requires either:
- Demonstrated execution of the codepath the ticket describes with no failure (add a probe; confirm it fires), or
- Two strategies attempted, both produced no failure under conditions matching the ticket.
- INCONCLUSIVE — neither reproduced nor confidence-grade negative evidence within ~20 wall-clock minutes. Distinct from NOT-REPRODUCIBLE: do not claim the bug is absent.
Repro evidence (mandatory)
Before declaring REPRODUCED, write a one-sentence comparison: "Ticket reports X fails; my repro produces Y; X ≈ Y because Z." Include this verbatim in the final report. Two failure modes to guard against:
- Surface mismatch. X must name the user-visible surface from the ticket (the specific dropdown, panel, screen, button). Y must be observed on that same surface, not an analogue. "Dropdown sorts wrong" vs "a helper function returns unsorted array" is NOT a match — the helper isn't a dropdown, even if they share code underneath. Shared code path is not sufficient; the bug must be observed where the OP says it lives.
- Synthetic-only repro. If your evidence depends on a filter, mu-plugin, or fixture you authored to inject the trigger condition, you have isolated the mechanism, not reproduced. Reproduce first in a stock environment (real theme, real files, default settings) through the surface the OP named. Synthetic injection is a debugging tool that comes after stock repro, for isolating the mechanism — not a substitute. If a stock repro is genuinely impossible, state in the report what property of real-world input is load-bearing and why it cannot be elicited without injection.
If either guard fails, keep working or classify INCONCLUSIVE.
Phase C — Fix
Default: test-driven development. Write a failing test in a standard WP test location (tests/phpunit/tests/... or tests/qunit/tests/...). Verify it fails. Implement the fix. Verify it passes.
TDD waiver — only for genuine UI/visual bugs where the underlying logic cannot be isolated into a function-level test. If waived, replace the report's "verification" field with the exact manual recipe (browser MCP steps or shell sequence) needed to re-verify the fix. Document the waiver reason in one sentence.
Scope
Minimal but complete. The fix touches only the lines that must change. Variables that become unused or names that become misleading as a consequence of the fix may be updated. Side-quests — refactoring, cleanup of pre-existing issues, restructuring "while we're here", touching adjacent code — are disallowed.
Hard cap: ~100 lines including the test. If the fix grows past that, stop. Classify REPRODUCED+UNFIXED with notes describing what a fuller fix would entail. A small reviewable diff is worth more than a sprawling one.
Verification
Run, in this order:
vendor/bin/phpcs <changed-files> — run as soon as the new test passes, on the modified source and test files only. WordPress core enforces phpcs cleanliness; failing it blocks merge. Run early so any style fixes happen before the broader checks.
- The broader test group the new test lives in (e.g.
vendor/bin/phpunit --group dependencies for script-loader tests). Confirm zero regressions.
- For bugs that surface through a concrete admin URL or front-end page: end-to-end verify through that real entry point (browser MCP + mu-plugin). A unit test that synthesizes the call sequence can pass while the real lifecycle still misbehaves.
Critical review
Before committing, review your own diff adversarially — as if reviewing a stranger's PR. Ask:
- Does the change exceed the minimum needed to fix the ticket? Strip any drift.
- Does the test fail without the fix and pass with it, and exercise the surface the bug actually occurs on (not a synthesized analogue)? Trace the call chain from the user-visible surface to the code you changed; the test must sit on that chain. A red-then-green test on the wrong layer (e.g. a helper the user surface doesn't even call) proves nothing about the user-visible bug.
- Are there assumptions in the fix that aren't load-bearing for the test? Are there callers/contexts that could rely on the prior behavior?
- Did running phpcs / the regression group / end-to-end verification reveal anything you glossed over?
- What would you push back on if a teammate sent this PR?
Address what you find. If a concern can't be resolved within scope, capture it in the report's notes field rather than expanding the diff.
Phase D — Commit and report
Commit on the trac-<ticket>/<slug> branch with a WP-style message:
<Component>: <imperative summary>.
<2-4 paragraphs explaining the bug, the fix, and any subtlety>.
See #<ticket>.
Stage files explicitly (git add <file1> <file2>). Never git add -A — .envlite/ and other generated artifacts must not be committed.
Produce the final report as the last message of the conversation, 250 words max:
classification: <REPRODUCED+FIXED | REPRODUCED+UNFIXED | NOT-REPRODUCIBLE | INCONCLUSIVE>
worktree: <absolute path>
branch: trac-<ticket>/<slug> (commit <sha>)
root cause: <one sentence | n/a>
repro: <exact command/URL/recipe — when the ticket's wording could plausibly refer to more than one UI surface, enumerate each with its state: broken / unaffected / not-checked. Do not declare REPRODUCED+FIXED with any plausible surface in not-checked state.>
repro evidence: <"Ticket reports X; repro produces Y; X ≈ Y because Z" | n/a>
fix: <one sentence | n/a>
verification: <command + observed result, OR manual recipe + observed result>
test: <path to test file | waiver: <reason>>
files changed: <list | none>
notes: <≤3 lines on edge cases, surprises, reviewer caveats>
Additional resources
references/repro-strategies.md — detailed phpunit / qunit / browser MCP guidance, including WP test conventions, action-firing patterns, output capture via get_echo, ticket-snippet sanity checks (re-entrancy, broken samples), mu-plugin + browser end-to-end repro pattern, escalation rules, and probe technique for NOT-REPRODUCIBLE evidence.
references/worked-example.md — full walkthrough of Trac #50040 (datepicker footer localization) from setup through report, including the critical-reading-of-comments lesson.