WordPress / WordPress/contributor-toolkit
The checkout is a standard Git repository; the app is a porcelain over it
Nobody has claimed this yet.
- Dominant language
- JavaScript
- Stars
- 36
- Forks
- 13
- Avg merge
- 23h 19m
- Merged PRs (30d)
- 72
Description
The proposition
The site's checkout should be a standard Git repository that any Git tool can read, and the app should be a porcelain over it — a friendly face on real state, not a private state model that happens to live inside a .git directory.
Today the app is closer to the second thing. A pull request is "applied" as a diff carried onto the checkout, tracked in the app's own store rather than in the repository. A contributor's parked work is a single commit rewritten in place, invisible to anyone reasoning with Git. Conflicts are reported by a matching algorithm that answers a different question than git merge does, so the app's conflict count agrees with nobody — not with GitHub, not with a contributor's own command line. And the app assumes throughout that it is the only writer to the checkout, which is exactly the assumption that ends in lost work when something else touches the tree (#348).
Why this is the app's problem to solve
The app's goal is not to be used — it is to produce WordPress contributors. A contributor who grows into Git should find that their checkout was a real repository all along: their branches are branches, their work is commits, and the day they open it with Git itself, nothing surprises them. Under the current model, the moment of success is the moment of breakage — the app is a silo with an easy way in and no way out. This proposal turns it into a ramp.
It is also what the collaboration scope (#107) quietly requires. The mentor sitting next to a newcomer at an event is a Git user. If they open the newcomer's site with their own tools — to look at a diff, to fix a conflict in their editor — the app must neither corrupt what they find nor be corrupted by what they leave.
The contract: two invariants
- The app never creates a state that Git cannot understand. Work is commits on branches; conflicts are conflict markers in files; anything the app knows about the repository is discoverable from the repository.
- The app is never confused by a state that Git created. Commits made by hand, conflicts resolved in an editor, a branch checked out from another tool — the app tolerates the common cases and refuses the exotic ones loudly and early. "I don't understand this repository, and here is why" is an acceptable answer; silent corruption and quiet desync are not.
The second invariant is unbounded if taken literally, which is why it is scoped: tolerate what a contributor and their mentor will actually do, refuse the rest by name.
Two tiers, so the beginner pays nothing
The contract must not make the newcomer's app more complicated. The resolution is that interoperability is a property of the repository, not of the whole app:
- A managed checkout — created by the app — keeps today's guarantees exactly: shallow, strict invariants, "you cannot break anything." The newcomer notices only one change, and it is an improvement: the conflicts they see become the same conflicts Git would show, which teaches the real thing instead of hiding it.
- An adopted checkout — imported, or a managed one where foreign activity is detected — gets the contract: the app respects what it finds, refuses what it cannot represent, and drops the promises it can no longer keep (it will not rewrite your history, and therefore also cannot promise you will not break it).
- A managed checkout can graduate to adopted. Never the reverse.
The test for every design decision downstream: if serving the Git user adds a dialog, a decision, or a wait to the newcomer's path, the decision is wrong.
What ends under this contract
The applied-patch record held outside the repository — state Git cannot see and the app cannot defend (the failure class behind #348 and the stale-record machinery).Struck 15 September 2026. #356, the piece that would have ended it, is closed as not planned: since #458 shipped a pull request arrives as its own branch, so the app-held layer now covers only Trac attachments and does not justify the rework. The record stays outside the repository, and this contract is delivered with that exception rather than in full. #236 is the live consequence.- The two-way diff apply as the way a pull request reaches a checkout, and with it the conflicts that agree with nobody.
- The unconditional rewrite of the parked commit, which silently orphans anything a human committed by hand.
The pieces
Each ships on its own and will be filed as a sub-issue of this one, roughly in risk order: standing on a pull request's branch (reopening #290, sharing plumbing with #349); the merge engine whose conflicts are Git's conflicts (completing #226 with a stricter acceptance bar); merge-in-progress as a first-class state that every destructive flow respects; trunk updates that evaluate standing work instead of erasing it; the "this needs a rebase" path back to the pull request's author; importing an existing checkout; the recovery property the contract implies, so that parked work stays reachable when its reference does not (#348); and, parked, Trac attachments as commits (#356, now closed as not planned — see the struck entry above). The interoperability spikes — the least-travelled corners of the bundled Git implementation — live as checklists inside the pieces that need them, and they come first, because a failed spike changes the design.
Deliberately not proposed
Requiring Git on the host. Whatever Git the app runs on travels inside the app, and the contributor installs nothing; what changes is that the repository format it maintains is the standard one, so a host with Git gets full fidelity as a bonus, not as a requirement. Whether that bundled Git stays a library implementation or becomes a real Git binary is #364's question, and this contract holds either way.
Related
- #349 — a second consumer of the same foreign-ref plumbing
- #290, #226 — the earlier halves of this observation, recorded before the intent existed
- #108 — the branch model this contract extends rather than replaces
- #107 — the collaboration scope that makes interop a requirement, not a comfort
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Begin with related issues #290, #226, #348, #349, and #364, then review the bundled Git implementation and the listed contract pieces. Identify the first interoperability spike and decompose the managed/adopted checkout proposal into actionable sub-issues; done means the design is testable without adding complexity to the newcomer's path.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- git, javascript
- Domain
- desktop-dev, developer-experience, tooling
- Issue type
- Feature
- Difficulty
- 5/5
- Estimated time
- Over a week
- Activity status
- Active
- Clarity
- Needs clarification
- Newbie friendliness
- 25/100