pr-review-merge-prep
Skill1.1k starsChanged 8 days agoMITView on GitHub
Principles for rigorously reviewing a Symfony UX pull request and making it merge-ready. Use when reviewing PRs (one or a batch), verifying a submitted fix, retargeting a PR to another branch, or amending a contributor's work before merge.
- Deletes or force-pushes
- Commits and pushes
We split this skill into the rules it gives an agent
17 rules. About 0 are common, 14 are distinctive, and 3 are very specific to this project.
≈0
Common · estimate
Many files like this have a rule of this kind.
≈14
Distinctive · estimate
This author's own choices. Worth a look.
3
Very specific
Tied to their project's names, files or tools. Probably skip.
- Common
- Distinctive
- Very specific
- Distinctive: line 11
Read the whole discussion before judging: the linked issue, the PR comments, the reviews and the inline comments.
- Distinctive: line 17
Use three-state verification to judge a rework: base (bug present), PR as submitted (what it really does), final (fixed).
- Very specific: line 19
Check the branch target. Bugfixes, features and deprecations go to 3.x.
- Distinctive: line 23
Test composition: rebase onto the current base tip and rerun.
- Distinctive: line 25
Before blaming the PR for a failure, reproduce the failure on the base branch.
- Distinctive: line 34
Never let agents touch the main checkout.
- Distinctive: line 36
When an agent stalls or dies, resume it with a message restating where it was and which constraints apply.
- Distinctive: line 37
Remove each worktree with git worktree remove once its PR is merged or abandoned.
- Distinctive: line 45
When the rework sits in local commits on top of the contributor's, squash before rebasing.
- Distinctive: line 61
Write the description for someone who never saw the discussion: the problem in one paragraph, the fix in one paragraph, and a before/after …
- Distinctive: line 64
Put what changed during review in a new comment instead.
- Very specific: line 66
Keep link definitions and their uses in sync when editing .rst docs: DOCtor-RST fails CI on a link definition nothing references.
- Distinctive: line 76
Use TDD for every fix: failing test first, implementation second, full suite of the touched package last.
- Very specific: line 78
No em-dashes, no Co-Authored-By trailers, no credit to AI tools in what you write: code, commit messages, PR titles and bodies, review …
- Distinctive: line 79
Keep a factual tone in everything published: findings and evidence, no self-promotion, no filler.
- Distinctive: line 80
Never apologize for a late or missing review.
- Distinctive: line 84
Use plain English: common words, short sentences, one idea per sentence.
≈ “Common” is an estimate: at least 1 in 5 of the 308,232 skill files we read have a rule of the same kind, not the same words. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.
Files
PR review and merge preparation
Reviewing
- Re-derive the root cause from the code. Do not trust the PR title, the description or the author's branch analysis. Verify every claim yourself. Use small probe scripts when reasoning alone leaves doubt.
- Read the whole discussion before judging: the linked issue, the PR comments, the reviews and the inline comments. Compare each reviewer request with the current diff. Flag requests that were ignored or only partly applied.
- Challenge every added public or protected method, property, option, parameter, Twig function, Stimulus value and class. Write the use case with the API that already exists, and run it. Accept the addition only when that attempt fails, or only succeeds through internals the package does not promise to keep. An alternative that runs is not automatically a refusal: weigh whether a user would find it, what it forces them to get right by hand, and what it reports when it fails. Saving a few calls is not a reason: public API is carried for a whole major version, and a shortcut added today constrains every later change. Apply the same test to the API you add yourself during merge prep, and run it before making the pull request ready, not after.
- Report that attempt whichever way it went. When it failed, the report is what justifies the addition, and it is what makes the decision survive the discussion. When it succeeded, close with the working snippet rather than the refusal alone, so the author leaves with a solution instead of a no.
- Give the same scrutiny to changed behavior of existing public API. It is riskier than an addition, because nobody opts into it: establish what depends on the current behavior before accepting the change. A diff that edits existing fixtures, expectations or snapshots to make them pass is announcing such a change; treat that as the signal rather than as housekeeping.
- Revert-verify every new test: put the changed source back in its base state, keep the tests, and confirm they fail with the expected error, not another one. A test that also passes on base protects existing behavior; it does not prove the fix. Be clear about the role of each test.
- Undo a revert-verify without destroying the working tree.
git checkout <base> -- <paths>writes the base version to the index as well, so restoring withgit checkout -- <paths>reads back from that index and silently discards every fix made during review. Commit or amend first and restore withgit checkout HEAD -- <paths>, or copy the files aside and put them back by hand. Confirm withgit diff --statbefore trusting the next test run. - Use three-state verification to judge a rework: base (bug present), PR as submitted (what it really does), final (fixed). Run the same checks in the three states. The pattern of failures across the states is the proof.
- Validate the test harness before trusting it: run it on the known-broken state and watch it fail. A check that cannot tell "read the right thing" from "fell back to a default" proves nothing. Give each outcome its own marker.
- Check the branch target. Bugfixes, features and deprecations go to 3.x. 2.x only receives security fixes. Security fixes follow the
security-triageskill. Retarget a pull request opened against 2.x for anything else. - A config tree must not depend on anything external. Reject a bundle
Configurationclass orconfigure()method that reads container parameters (kernel.debug), env vars, another extension's raw config or machine state (extension_loaded(),class_exists()) into node defaults, normalization or validation: the same tree must come out whatever the environment or the machine, becauseconfig:dump-referenceand the generated config reference are built from it. Such a default belongs in the extension, behind adefaultNull()node whoseinfo()names the fallback, or behind a static'%kernel.debug%'default that the extension resolves. - List the edge cases and run them. Do not accept plausible reasoning when a probe can answer: nullable, union and interface types, empty input, recursion, encodings, interaction with sibling state.
- Check every symbol the change borrows from another package against that package's declared version constraint, in
composer.jsonand inassets/package.json(peerDependencies). A symbol added in the current release cannot be satisfied by the oldest version the constraint allows, so the call site needs an existence guard, or the test belongs in the package that owns the code. The lowest-dependency CI job is only the symptom: the same code is broken at runtime for anyone on that older version. - Test composition: rebase onto the current base tip and rerun. When sibling PRs touch the same area, also run the combined result.
- Account for every failing CI job: reproduce it when possible and classify it as caused by the PR or pre-existing. A red job nobody can explain is a finding. When comparing against a baseline run, check its date and not only its conclusion: a branch tip that saw no push for hours keeps a stale green run, while CI installs current dependencies on every run, which makes an unrelated change look like the regressor.
- Before blaming the PR for a failure, reproduce the failure on the base branch. Local environments have pre-existing failures.
- Check documentation duties in both directions. A feature needs its
CHANGELOG.mdentry and its documentation insrc/<Package>/doc/*.rst, in the same PR; a deprecation also needs its upgrade note. A plain bugfix carries no CHANGELOG entry: the CHANGELOG lists features, deprecations, backward-compatibility breaks and bugs big enough that someone upgrading needs to know. Check that an entry sits under the topmost, unreleased version heading of the package'sCHANGELOG.md. Contributors routinely file it under a version that already shipped, because that was the branch they opened against. - Test at the layer where the defect lives. Assertions on intermediate state (service wiring, generated config, the HTML a snapshot pins) cannot catch a bug that only appears in a later phase (compiled container, the browser, a real Stimulus controller). When the bug lives there, the test must go there, which for JS behavior can mean a Playwright test.
Batch review
- To review many PRs, run one subagent per PR, each in its own git worktree. Parallel runs must never share mutable state.
- Put each worktree in
.claude/worktrees/pr<number>(listed in.git/info/exclude, sogit statusstays clean). Create it detached, then rungh pr checkout <number>inside it: that creates a local branch tracking the contributor's fork, so the push the maintainer runs at the end goes to the right place. Never fetch the head withgit fetch upstream pull/<number>/head:..., which leaves the branch with no push remote. - Give each worktree its own installs (
pnpm installat the root,composer updatein each touched package). Never link the main checkout'svendor/ornode_modules/into a worktree: an installer that rewrites files in place propagates through the link and breaks every checkout at once. - Never let agents touch the main checkout; treat it as owned by the user, along with the branches it has checked out.
- Give every agent the same brief: the full single-PR standard above plus the batch-wide checks (branch target, composition with sibling PRs), returning a structured report with verdict, root cause, branch conclusion, and findings with evidence.
- When an agent stalls or dies, resume it with a message restating where it was and which constraints apply; do not restart the review from scratch.
- Remove each worktree with
git worktree removeonce its PR is merged or abandoned.
Making it merge-ready
- Preserve the contributor's authorship and ship one commit. Squash the PR into a single commit authored by the contributor: amend into their commit, or rebuild it with
git commit -C <their commit>, which keeps their author. Update the commit message to cover what was added; a message must always match its diff. - Authorship is two fields. Amending keeps the author and rewrites the committer, and the committer is what the forge shows next to the commit and what stays in public history. Never pass a placeholder identity to
git commitorgit rebase: the repository's configured identity is already the right one. Check withgit log -1 --format='%an <%ae> / %cn <%ce>'before handing the branch over, because merged history cannot be fixed. - Fixes added during review follow the same TDD rule as the original bug: failing test first, then the fix.
- Retargeting to another branch is a rewrite, not a copy: express the fix in the target branch's code shape and conventions (available APIs, minimum PHP, test style, docblock metadata or attributes, tool versions). Rebuild tests that depend on things the target branch lacks. When a fix lands on 2.x and 3.x needs a different shape, record the known merge-up resolution in the pull request description, under a
Merge-up to 3.x:paragraph, so the merger does not have to rediscover it. - When the rework sits in local commits on top of the contributor's, squash before rebasing. Otherwise the rebase replays the original commit first and raises conflicts against content the rework already replaced.
- A conflict-free rebase can still produce broken code: an import the source branch happened to have, a test runner that ignores newer test syntax, a
dist/built from the old sources. The test run decides, not the rebase. Rebuilddist/after every rebase that touchesassets/src/. - Rebase onto the current base tip (
git fetch upstream && git rebase upstream/<base>) and confirm the parent commit equals it. Check again just before handing the branch over: active repositories move. Run rebases and merges non-interactively (GIT_EDITOR=true). - A clean local style run is necessary, not sufficient. fabbot can be a superset of the local tool, and it lints every file the PR touches, including lines the PR did not write, so pre-existing debt in a touched file becomes this PR's failure. Judge the local tool by its exit code: output formats vary, and a grep for its listing can report clean while the tool found issues.
- Read the reports of static analysis and style tools as hints, not as verdicts. They match patterns, they do not know what the code means, and false positives are normal. Never add an annotation, a cast or any other contortion whose only purpose is to silence a tool, and never patch a line the pull request does not touch to make a check pass. Judge each report by what the code does at runtime, and say so when the right answer is to leave the check red.
- Run scoped tests while iterating and the full suite of each touched package before calling it done; never run every package at once. PHP suites run per package:
cd src/<Package> && composer update && php vendor/bin/phpunit, and the same fromsrc/<Package>/src/Bridge/<Bridge>for a bridge. JS runs per package too:cd src/<Package>/assets && pnpm run test:unit(andpnpm run test:browserwhen the change affects browser behavior). After any change underassets/src/, runpnpm run buildin that package and commitdist/; CI fails whendist/does not match a fresh build. If an assertion depends on the installed dependency version, use feature detection instead of assuming one version. - Retarget before the push, never after. CI reads the base branch from the pull request event at push time, so pushing a branch rebased onto a new base while the PR still points at the old one makes every job diff against the wrong branch: a diff of thousands of files, style checks on code the PR never touched, and a wall of red that a small change cannot explain. Rerunning does not help, since the event is unchanged.
The maintainer pushes; never run
git pushand never merge the pull request. Hand the branch back as exactly two lines:cd .claude/worktrees/pr<number> git push --force- Update the PR's metadata to match what it has become: title and package prefix (
[LiveComponent]), header table, description, labels and base. On a PR that sat, the header table can claim a feature the review turned into a fix, the description keeps wording the review already corrected, and the prefix can name the wrong package. Leave exactly oneStatus:label, and none the diff no longer justifies. - Editing the PR, posting a review and commenting are outward actions published under the maintainer's account. Show the text and ask before the first one, unless the maintainer already asked for it.
- Write the description for someone who never saw the discussion: the problem in one paragraph, the fix in one paragraph, and a before/after table for a behavior change. Cut what the diff already shows. The documentation itself ships in the same PR, under
src/<Package>/doc/. - Apply corrections instead of suggesting them. The branch is being rewritten anyway, so a review comment asking for a one-line change is a pointless round trip. This covers the contributor's own code comments and test names when they describe something the code does not do.
- Routine merge prep needs no comment at all: a rebase, a changelog fix, a constraint bump and a style fix are all visible in the diff. Comment when the reader has to decide or react.
- Put what changed during review in a new comment instead. Those notes matter to the people following the thread and stop mattering once it merges, while the description outlives it.
- Keep CHANGELOG entries to one line each, naming the new public surface and nothing more. Add a new entry at the end of the version's list, or next to the entries it relates to. Reasoning, comparison tables and usage guidance belong in the documentation. A CHANGELOG entry that has to explain itself is a sign the documentation entry is missing.
- Keep link definitions and their uses in sync when editing
.rstdocs: DOCtor-RST fails CI on a link definition nothing references. - Apply the house rules to inherited content too: when amending someone else's commit, remove what the rules forbid instead of keeping it. An attribution trailer already in the contributor's commit is theirs: leave it unless the maintainer asks to remove it. Scan the added diff lines for violations before handing the branch over.
- Finish on GitHub: post review feedback as a real review with evidence, close replaced issues with a factual explanation, and correct wrong claims left in threads.
- When closing on the merits, state the decision and the reason and stop. Do not invite a fresh pull request, since that reopens the same discussion under a new number. When closing a stale pull request nobody rejected, a short line welcoming a fresh take is the right tone instead.
House rules
- Write code comments sparingly, only where they add value the code cannot express. A test method or a test helper carries no docblock and no comment narrating it: its name and its assertions say what it checks. The comment that stays in a test is the one saying why a fixture looks wrong on purpose. Never reference issues or pull requests in code or tests.
- A prose docblock opens with one line that summarizes, then a blank line, then the details. Before reshaping a comment into that form, ask whether it should exist at all.
- Do not wrap prose at a column. A line ends where a sentence, a paragraph, a list item or a code sample ends, and a sentence stays on one line unless it runs very long, in which case it breaks where a clause ends: a line break has to mean something, and a column count is not a meaning. The same holds for a
@paramdescription. - Use TDD for every fix: failing test first, implementation second, full suite of the touched package last.
- New
test*methods follow their branch: no return type on 2.x, where fabbot rejects one;: voidon 3.x, where php-cs-fixer'svoid_returnrule adds it and fabbot's check is disabled. - No em-dashes, no
Co-Authored-Bytrailers, no credit to AI tools in what you write: code, commit messages, PR titles and bodies, review comments, issue comments. - Keep a factual tone in everything published: findings and evidence, no self-promotion, no filler.
- Never apologize for a late or missing review. Thank the author for their patience and stop there. Do not editorialize about the project's failure to review; credit what the author did well instead.
- Comments are published under the maintainer's own account, so write their past review comments in the first person and everyone else's in the third.
- Private methods go at the end of the class, after all public and protected ones. Do not move existing ones in the same patch.
- Do not call a Symfony or third-party method or constructor with PHP named arguments, in a patch you write or in one you review. Parameter names are outside the backward compatibility promise except for the constructors of attribute classes (note 10 of https://symfony.com/doc/current/contributing/code/bc.html), so a renamed parameter breaks such a call in a minor release. Pass the intermediate defaults explicitly to reach a later optional parameter. Named arguments stay fine for attribute constructors and for PHP's own functions, whose parameter names are part of the language.
- Use plain English: common words, short sentences, one idea per sentence. Avoid idioms, cultural references and rare vocabulary. Most readers are not native speakers.
Reporting
- Lead with the verdict. Then list findings ranked by severity, each with its evidence: what was run, on which state, and the exact failure text.
- Separate regressions from pre-existing gaps, and blockers from polish. Say which findings the merger must act on.
- State what was not covered as plainly as what was.
- In a batch, relay each verdict as soon as it lands. When all reports are in, give a summary table: one line per PR with its verdict and target branch.
- For the detailed pass, present one PR at a time and wait for a go-ahead keyword (such as "next") before the following one. This keeps each review open for discussion and lets fixes happen before moving on.
Trending
Quiet since September.about the whole repository
Who's talking about symfony/ux
- symfony/symfony“[Form] Add `MultiStepType`”GitHub · Jan 18, 2025 · 40 reactions
- twigphp/Twig“Add an `html_attr` function to make outputting HTML attributes easier”GitHub · Dec 6, 2023 · 12 reactions
- twigphp/Twig“Introduce CVA to html-extra”GitHub · Mar 9, 2024 · 6 reactions
- chartjs/Chart.js“Rollup build failing since Chart.js 3.9”GitHub · Aug 15, 2022 · 6 reactions
- contao/contao“Refactored backend controllers and backend module fragments”GitHub · Feb 14, 2021 · 6 reactions
The biggest 5 of 44 mentions linking to the repository that our crawl found. Each line is their own headline.
See all 44
Talked about on GitHub and Stack Overflow (44 times).
On GitHub · 41
- Jan 18, 2025[Form] Add `MultiStepType` — symfony/symfony40 reactions · 24 comments
- Dec 6, 2023Add an `html_attr` function to make outputting HTML attributes easier — twigphp/Twig12 reactions · 35 comments
- Mar 9, 2024Introduce CVA to html-extra — twigphp/Twig6 reactions · 6 comments
- Aug 15, 2022Rollup build failing since Chart.js 3.9 — chartjs/Chart.js6 reactions · 2 comments
- Feb 14, 2021Refactored backend controllers and backend module fragments — contao/contao6 reactions · 2 comments
36 more on GitHub
- Jul 14, 2022Don't update package.json or controllers.json if vendor not updated — symfony/flex5 reactions · 6 comments
- Jun 4, 2025Improve `Config::getMergedConfig()` perf by ~15% — gehrisandro/tailwind-merge-phpsays it tested it3 reactions · 3 comments
- Nov 25, 2021Add `make:twig-component` maker — symfony/maker-bundle3 reactions · 6 comments
- Aug 8, 2026Switching away from `scrivo/highlight.php` in the code element — contao/contao2 reactions · 6 comments
- Jan 9, 2026[UX-Icon] Add ability to lock icon — Sylius/Sylius2 reactions · 3 comments
- Aug 18, 2024[RFC] Add PreLexerInterface — twigphp/Twig2 reactions · 3 comments
- Apr 18, 2024Twig functions becoming internal — twigphp/Twig2 reactions · 26 comments
- Sep 27, 2026Complete Twig function hash keys and values from the PHP parameter array shape — Haehnchen/idea-php-symfony2-plugin1 reaction
- Sep 17, 2026Fresh install using docker is failing with errors unable to access the database file. — SolidInvoice/SolidInvoice1 reaction
- Apr 10, 2026Update Symfony UX packages to 3.x — symfony/ux.symfony.com1 reaction
- Jan 20, 2026Add `html_attr_relaxed` escaping strategy — twigphp/Twig1 reaction · 3 comments
- Jan 16, 2026Import and migrate Toolkit recipes here — symfony/ux.symfony.com1 reaction
- Jan 12, 2026Support for ComponentName/index.html.twig anonymous templates — Haehnchen/idea-php-symfony2-plugin1 reaction · 1 comment
- Nov 22, 2025Fix declaration of twig extension as runtime — symfony/mercure-bundle1 reaction · 1 comment
- Nov 21, 2025feat: allow PHPUnit 9 — spatie/phpunit-snapshot-assertions1 reaction · 1 comment
- Oct 8, 2025[UX][Bug] Avoid considering "false" as a truthy value for "synchronize_package_json" — symfony/flex1 reaction · 6 comments
- Jul 16, 2025[Panther] Configure env `PANTHER_DEVTOOLS=0` — symfony/recipes1 reaction · 2 comments
- Jul 13, 2025[PropertyInfo] `PhpStanExtractor::getDocBlock` fallback to `getX()` PHPDoc even if property `X` exists (but has no PHPDoc) — symfony/symfony1 reaction · 2 comments
- May 7, 2025[FrameworkBundle] Ensure `Email` class exists before using it — symfony/symfony1 reaction · 2 comments
- Sep 19, 2024Auto-Set Locale from HTML-lang-attribute when used with international routing — FriendsOfSymfony/FOSJsRoutingBundle1 reaction · 1 comment
- Sep 18, 2024vite_entry_link_tags nonce attribute — lhapaipai/symfony-vite-dev1 reaction · 8 comments
- Jan 17, 2024[DomCrawler] Allow to add choices to single select — symfony/symfony1 reaction · 3 comments
- Dec 28, 2023🚧 New autocomplete — Sylius/Sylius1 reaction · 1 comment
- Nov 28, 2023Removing yarn — symfony/symfony-docs1 reaction · 3 comments
- Nov 9, 2023[AssetMapper] Only download a CSS file if it is explicitly advertised — symfony/symfony1 reaction · 2 comments
- Jan 17, 2022[WIP] Integrating UX docs more closely into Symfony docs — symfony/symfony-docs1 reaction · 6 comments
- Jan 11, 2022[HttpFoundation] : add support for text/vnd.turbo-stream.html — symfony/symfony1 reaction · 4 comments
- Feb 27, 2025How can we update the `TurboStreamListenerRenderer` configuration? — symfony/mercure-bundle0 reactions · 4 comments
- Jan 25, 2025Add contributing steps — symfony/stimulus-bridge0 reactions
- Jan 5, 2025Universal Module Definition (UMD) vs ES Modules (ESM) — Leaflet/Leaflet.Editable0 reactions · 2 comments
- Oct 11, 2024[AssetMapper] Document how to install in China (JsDelivr is not accessible reliably) — symfony/symfony0 reactions · 6 comments
- Apr 21, 2024Feature request: stimulus-helper package, a package for rendering stimulus attributes easily — ajaishankar/stimulus-reactive0 reactions · 3 comments
- May 4, 2023Upgrade Symfony UX — Demokratis-ch/demokratis-ch0 reactions
- Dec 10, 2021Symfony UX — Coderockr/backstage0 reactions
- Sep 29, 2021Handle state refresh without poll — sandoche/Symfony-ux-live-component-todo-list0 reactions
- Jun 17, 2021Use typing effect for pages' titles — Elao/elao_0 reactions
On Stack Overflow · 3
- Jul 21, 2023How to add DateTime adapter to symfony/ux-chartjs?2 votes
- Feb 22, 2023Symfony 5 - Override Form Type Options1 vote
- Jul 27, 2026Symfony UX Live Components: File Validation Only Triggers After Other Fields Pass Validation0 votes
Other versions1 project uses a version of this.
Word for word · 0
None found.
Edited · 1
- symfony/symfony★ 31k59% same
Wrote their own version · 0
None found.
How matches are found. Word for word: the exact same text. Edited: shares passages with this file (“99% same” means 99% of its five-word phrases also appear here; 10% or more counts). Their own version: similar meaning, or, weakest, the same name. We can’t tell which project had it first. Projects are counted among the 200 most-starred candidates, and mentions are links to the repository we found, up to 50 per site, so both counts are a floor.
In this repository7 other files it gives its agents.
- AGENTS.mdAGENTS.md
- CLAUDE.mdCLAUDE.md
- merge-upSkill
- pr-authoringSkill
- security-triageSkill
- symfony-security-reviewSkill
- symfony-ux-toolkit-kitSkill
Does it work?
Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.
Reviews can't be read right now.
Your trail
Files you open will collect here, so you can find them again.
Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.
Now do this for your own.
Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.

