TYPO3 Core Patch Checkout
Skill: typo3-core-patch-checkout
Get a patch under review on review.typo3.org into a core checkout and out again — onto the branch it targets, into a git worktree beside it, cherry-picked onto current code, or as the base for extending somebody else's change. Trying one out, seeing whether it still applies, leaving the checkout clean. Pushing is typo3-core-patch-development.
Markdown source#
---
name: typo3-core-patch-checkout
description: 'Get a patch under review on review.typo3.org into a core checkout and out again — onto the branch it targets, into a git worktree beside it, cherry-picked onto current code, or as the base for extending somebody else''s change. Trying one out, seeing whether it still applies, leaving the checkout clean. Pushing is typo3-core-patch-development.'
compatibility: Needs the typo3-dev-companion MCP server, which owns every lookup this workflow routes to and publishes this skill together with the references/base.md it opens on. Install it from github.com/TYPO3/dev-companion and run typo3-dev-companion install in the project. A copy taken out of that repository's skills directory alone has neither the tools nor that base file.
---
# TYPO3 Core Patch Checkout
Put one change under review into the checkout, in the state it is in. Keep this
skill as routing and stopping rules. The refs, the remotes and the commands are
lookups. A copy of them here goes stale in somebody else's checkout, and nothing
reports it.
This is a workflow of its own because of what it must not do. A patch that no
longer applies is a finding: the branch moved under a change nobody rebased. A
session that quietly resolves its way to something that compiles has destroyed
that finding. It has produced a patch nobody wrote. So every step below has an
end, and to reach one is a result.
**One change, because the work needs it on disk.** What a change touches, what
its message says and where its review stands come back without a fetch. That is
step 2 below. So you triage a shortlist before you open this workflow at all.
You reach the checkout for the one change that survived the triage.
Afterwards nobody can tell refs you pulled in for a read from the branches with
the reader's own work. One session fetched eight open changes into somebody's
checkout, and the user stopped it over them.
## Establish the change before you touch the checkout
1. Work through [references/base.md](references/base.md), which fixes the order
every task here starts in.
2. `typo3_gerrit_lookup` with the change number or the Change-Id. Use the issue
number where you reached the change through its issue. Four things it answers
decide everything below.
The **branch the change targets** is what you apply it onto. It is regularly
not the one you stand on. The **patch set that is current on the server**
matters because an older one is still fetchable. An older one silently
reviews a revision nobody looks at. The **status** matters since MERGED and
ABANDONED are both answers that end the work. The **commit** says afterwards
whether the checkout holds the revision under review.
3. `typo3_rule_lookup` for the Gerrit workflow. It carries the ref you fetch a
patch set by. That is the one thing about a core change fetch nobody can
guess. It says which remote the ref is on, which is not the one the checkout
fetches from. It says the form the third way in below takes. It says what a
patch set opened on somebody else's change owes its author.
Those are sections of one page, and a search returns the one your words
matched. So read it whole where the fetch is the task, and always on the
fourth way in. That is `typo3_rule_lookup` with
`documentId="core/contribution/gerrit-workflow"`, which also stands as
`typo3://guides/core/contribution/gerrit-workflow`.
## Four ways in
The patch goes onto the branch it targets in the checkout you stand in, or into
a worktree beside it. Or it goes onto current code as a commit of your own. Or
it goes under work of your own, as the base you carry it on. The request usually
says which.
The first two hold the patch as its author wrote it. Whether the checkout is
free decides between them. The branch path needs it to itself. A worktree leaves
the current branch and everything uncommitted on it alone.
A worktree does not save that work, it moves it. It starts without the installed
dependencies, which git ignores and so does not bring. So no suite runs in it
until you install them there.
`typo3_test_run_guide` states that precondition above its suites. Beside it
stands the one check whose file list comes from git. In a worktree it reports
success after it read nothing. Read both before you run anything in one.
The third answers a different question: whether the change still applies to
current code and still passes there. "Cherry-pick it onto main" asks for it. It
takes the patch off the code its author wrote it on. So what the checkout then
holds is a commit this session made, not the revision under review.
That commit gets a name, `review/<change number>`, because the rest of the work
reads it. A reader can tell a named branch from the checkout's own work. A
reader can find it again after anything moves, and remove it on purpose. Where
the checkout is not free, this way in takes the worktree as the second does. You
create the branch there.
The fourth is the one the other three read as an obstacle. The patch is the
base, and work of your own goes on top of it. "Extend their patch with ours",
"amend somebody else's change" and "add this to the change" all ask for it. What
comes out is a further patch set on that change rather than a change of yours.
It uses the third's branch, started at the fetched patch set instead of at
current code. The section below says what it owes before you commit anything.
## One change, or a chain of them
`typo3_gerrit_lookup` answers a `chain` with every change read by name. A chain
longer than one open link changes the whole of what follows. A large refactoring
arrives this way, and the request says so. "Rebase the chain" names it outright.
Read the chain before the fetch and take four things off it.
- **Which links are still open.** You have to carry those, and their number is
how many commits the move is. The merged ones below them are already on the
branch, or should be.
- **Whether the merged link below is an ancestor.** `chainedAt` against
`patchSet` on that link says it. A link that shows `chainedAt` 18 and
`patchSet` 23 merged as a revision the chain never sat on. So
`git merge-base --is-ancestor` answers no, and a share of the conflicts below
follow from that one difference. Nothing in a checkout says it.
- **The move is a range.** Where one change is `git cherry-pick FETCH_HEAD`, a
chain is `git cherry-pick BASE..TIP`. `git rebase --onto origin/main BASE TIP`
is the same thing. The branch keeps the `review/<change number>` convention.
It takes the number of the **tip** change, which is the one the request named.
- **Every later amend is two steps.** You cannot amend a conflict resolution or
a fixer finding that belongs to the lower commit from the tip. Detach to it,
amend, apply again what sat on top, and move the branch. Budget for it rather
than discover it at the end.
The stopping rules below are for somebody else's patch, and a chain is regularly
the requester's own. The answer's owner field settles whether the change owner
is the person who asks. Where they are, the rule that stops you from an absent
author's decisions has nothing left to protect. To resolve past a handful of
hunks is the right call. Say in the result that you did, and on whose change.
## Before you change the checkout
Establish these three, in this order, and stop at the first that fails. The
first and the third are about the working copy the patch goes into. On the
worktree path that is the worktree.
- **The working tree is clean.** Afterwards nobody can tell uncommitted work and
a fetched patch set on top of each other apart. What a carry does to the
mixture is not recoverable from the checkout alone. Stop and say what the
uncommitted work is. Do not stash it as a convenience.
Where that uncommitted work is what stands in the way, the worktree is the way
past it.
On the fourth way in it is the material instead. Commit it where it stands,
before you fetch anything. Say in the answer which branch and which commit
that is. Then it has a base, which is what the file by file rule below reads
against.
- **The target branch is there and current.** A change that targets a release
branch, carried onto the wrong one, produces conflicts. Those are an artefact
of the mistake, and they look exactly like a stale patch.
- **Where you are is where you can get back to.** Write down the commit the
checkout is on before anything moves it. A worktree moves nothing, so what you
write down there is where the worktree is. To remove it is the whole of the
undo.
## Fetch and apply
Fetch the patch set the server says is current, and put the checkout on it. It
is somebody else's commit. It belongs on no local branch of yours for as long as
it is still that commit. The detached checkout and the worktree say that.
The fetch is the same on every path, and only its destination differs. That is
the checkout detached onto the commit, or a worktree on it while your branch
stays where it is. Or it is the branch named for the change with the commit
carried onto it. Or it is that same branch started at the commit itself.
Then establish what you hold before you judge anything about it. The checkout's
commit is the change's current revision, or it is not, and only the second needs
an explanation. A carry onto current code is the second by construction. The
section below says what you have to say instead.
## Carry it onto current code only where the work needs it
Carry the patch when the change does not sit on current code and the work needs
it to. That is when you run the suites or read it against code that has since
changed. It is also when the user asked for the carry. Not as a matter of
course. A patch read against the code its author wrote it on is the patch its
author wrote. A move is a step that can go wrong.
A rebase of the fetched commit and a cherry-pick onto current code are one move
under two names. A core patch is exactly one commit. A chain is the range above.
Either way, what comes out is a commit made here that exists nowhere else. That
is why it sits on a branch named for the change, and why the undo below deletes
that branch. The command form belongs to the page the fetch is on.
Where it applies clean, say so, and only that. A clean rebase is not a patch
that still holds. Where the change rewrites, moves or deletes a class, the
rebase reverts a fix `main` landed there. There is no conflict to show for it.
The files are different ones.
`typo3_rule_lookup` with `documentId="core/contribution/rebasing-a-stale-patch"`
holds the four steps that settle it. You owe them before the result says the
patch survived.
Say which commit every result after this is about. The carried commit's hash is
not the patch set's. A finding that quotes the local one without a word about
that names a revision nobody else can look up.
Where it conflicts, [references/checklist.md](references/checklist.md) decides
whether you resolve or stop, one conflict at a time. Read it at the first
conflict rather than after you resolved a few. The rule it carries is about what
you may know, and you cannot apply it backwards.
## Carry your own work onto the patch set
The ask comes first, and the author answers it. To extend a change under review
is ordinary practice, and you ask before you do it. So a session that does not
know whether somebody made the ask makes it rather than assumes it.
Where the ask has no answer, the answer is a comment on the change. The same
holds where what you would change is the author's own decision rather than a
correction. This workflow then stops with what it found. That is a result and
not a failure to deliver.
The page step 3 reads whole says the rest. It says what the amend does to the
author and to the committer. It says what the upload owes the author on the
change itself. It says what stays fixed whoever pushes. Read it before the first
commit and not before the push. The ask is not a step you can take afterwards.
Then the work moves one file at a time, and the rule is the one that already
decides a conflict. The checklist carries both halves. It says whether a file of
yours can go onto the patch whole. It says whether a hunk where the two collide
is yours to write.
Say what came from where. Say the patch set the branch started at and the commit
your own work was on. Say which files came from which side. The author will read
the diff between two patch sets, and that diff says what moved and never why.
**Once the result stands, invoke `typo3-core-patch-development`.** The push
belongs to that workflow. The amend, the message and the question of what goes
up visible to everyone are its steps. What crosses over is the change number,
the patch set the branch started at, and the result's branch. It is every
decision you took on the author's behalf. That last one is what the comment on
the change has to carry.
## Stopping is the normal ending
When a rule above ends the work, undo what you started rather than leave the
checkout half-way. The section below is that undo, and it is the same one that
ends a run that went fine. A checkout left half-way through a carry is a trap
for whoever opens it next, you included.
Report what you found and not what you attempted. Report the change, its patch
set, its target branch, how far it got, and the specific thing that stopped it.
That is the files that conflicted, the hunks, and why the change alone did not
decide them. That report is the useful outcome. Somebody who rebases the patch
properly starts from it.
## Once it is in and applies
`typo3_test_run_guide` with the paths the change touches names the suites that
can fail on it and their targeted invocations. Run them through the checkout's
own runner. A suite run through an installed binary is a result nobody can
reproduce. A check that inspected no files is not a green. The second is what a
worktree does by default. The same answer says what you have to install there
before any suite runs at all.
Say which branch and which patch set every result is about. Say which working
copy it ran in where that was a worktree, and which commit where you carried the
patch. A green reported without them has no owner the moment somebody pushes a
new patch set.
## Put the checkout back
A checkout on somebody's patch set is not a state to leave behind. It is not a
state to start the next piece of work from either. To restore it is a step of
its own, whether the patch applied or stopped. On the branch path it is these
six, in this order, because each part makes the next one possible.
There is one ending it does not run for, and it is the handover above. The work
continues on the review branch. So steps 2, 3, 5 and 6 would move the checkout
off the result or delete it.
What that ending owes is steps 1 and 4 and then the answer. That is the branch,
the commit, and what on it is not the author's. It is that you left the checkout
there on purpose. Neither "restored" nor silence is what such a session reports.
1. **End whatever is in progress first.** A carry that is half applied, or one
that stopped in a conflict, owns the tree until you abort it. Every later
step fails against it in a way that reads like something else.
2. **Return to the branch you recorded at the start**, not to whichever branch
looks right. Where you only fetched the patch, it was on no branch. So to
leave it loses nothing that is not still on the review server. Say the commit
in the answer, because that is the only local name it had.
3. **Delete the branch you carried the patch onto**, where there was one. Git
refuses the ordinary deletion, and the forced one gets past that refusal
rather than answers it. `typo3_gerrit_lookup` with `commit` and the tip the
branch is on answers it. A commit somebody pushed comes back as the change it
is a patch set of, superseded patch sets included. The ref beside it is the
undo, and you say it in the answer before the branch goes.
An empty answer is a commit nobody pushed, or one an anonymous reader may not
see. Nothing brings that one back. A carried commit is the second kind by
construction. So read what is about to go: what you resolved in a conflict is
in it and in nothing else. Ask the same of any other ref this workflow has to
remove. Nothing says in advance which of the two it is.
Then say in the answer that the branch is gone. "The checkout is back on its
branch" is true with the review branch still beside it.
4. **Establish that nothing of the patch remains.** An aborted carry can leave
files the change added untracked. They belong to no commit and to no branch.
The next suite run picks them up and fails for a reason that has nothing to
do with anything. What the working tree holds and what git does not track are
two different questions, and you ask both.
5. **Update the branch from the remote it fetches from, not from the review
server.** These are two different URLs on a core clone. `typo3_rule_lookup`
for the Gerrit workflow says which is which. The change refs live on only one
of them. Take the update as a fast-forward. A merge commit on a local branch
that tracks the core is a state nothing here asked for.
6. **Bring the installed dependencies back in step with the branch.** A move
between a patch set and current code can change what the lock file pins. A
suite run against the other revision's dependencies fails for a reason that
is not in the diff. This is the step people skip and then spend an hour on as
a test failure.
The worktree path ends shorter, and the difference is not a shortcut. Nothing
moved the branch, so steps 2, 5 and 6 have nothing to put back. They are about a
checkout that went somewhere.
What remains is to end whatever is in progress and then remove the worktree. Git
refuses that while anything in it has a modification or no tracking. That
refusal is step 4 one layer out. To force past it throws away the only copy of
whatever you resolved in there. Step 3 stands there too, because a removed
worktree leaves the branch you created it on behind.
Say the end state in the answer: which branch, which commit, and that the tree
is clean. On the worktree path, say that the worktree is gone. Or say where it
still is and why you kept it on purpose. Where you carried the patch, say that
you deleted the branch you carried it on. "Restored" without those is the claim
rather than the result.
This skill owns how a change under review gets into a checkout and back out of
it. What that covers, in order:
- Find it, and fetch the patch set.
- Put it on the branch it targets, into a worktree beside it, or onto current
code on a named branch. Or put it under work of your own where you extend that
change.
- Resolve what the change itself decides, and stop where it does not.
- Leave behind a clean branch current with its remote, no worktree of its own
and no branch it made. It owns the undo as much as the do, and the undo runs
whichever way the rest went. The one exception is the branch it hands over,
which is the result.
It does not own the judgement of the patch. Where the request is to say what is
wrong with it, `typo3-core-patch-review` owns that. That skill starts from the
working copy this one leaves the patch in, before the undo runs. That is the
worktree, the checkout or the review branch. Carry across which commit the
findings will be about.
It does not own a change to what is on the review server either. To amend a
change into a new patch set and push it belongs to
`typo3-core-patch-development`. Carry over the change number and the patch set
you fetched. Carry over whether you had to carry it onto current code to apply.
Carry over what you decided on the author's behalf.
markdown
---
name: typo3-core-patch-checkout
description: 'Get a patch under review on review.typo3.org into a core checkout and out again — onto the branch it targets, into a git worktree beside it, cherry-picked onto current code, or as the base for extending somebody else''s change. Trying one out, seeing whether it still applies, leaving the checkout clean. Pushing is typo3-core-patch-development.'
compatibility: Needs the typo3-dev-companion MCP server, which owns every lookup this workflow routes to and publishes this skill together with the references/base.md it opens on. Install it from github.com/TYPO3/dev-companion and run typo3-dev-companion install in the project. A copy taken out of that repository's skills directory alone has neither the tools nor that base file.
---
# TYPO3 Core Patch Checkout
Put one change under review into the checkout, in the state it is in. Keep this
skill as routing and stopping rules. The refs, the remotes and the commands are
lookups. A copy of them here goes stale in somebody else's checkout, and nothing
reports it.
This is a workflow of its own because of what it must not do. A patch that no
longer applies is a finding: the branch moved under a change nobody rebased. A
session that quietly resolves its way to something that compiles has destroyed
that finding. It has produced a patch nobody wrote. So every step below has an
end, and to reach one is a result.
**One change, because the work needs it on disk.** What a change touches, what
its message says and where its review stands come back without a fetch. That is
step 2 below. So you triage a shortlist before you open this workflow at all.
You reach the checkout for the one change that survived the triage.
Afterwards nobody can tell refs you pulled in for a read from the branches with
the reader's own work. One session fetched eight open changes into somebody's
checkout, and the user stopped it over them.
## Establish the change before you touch the checkout
1. Work through [references/base.md](references/base.md), which fixes the order
every task here starts in.
2. `typo3_gerrit_lookup` with the change number or the Change-Id. Use the issue
number where you reached the change through its issue. Four things it answers
decide everything below.
The **branch the change targets** is what you apply it onto. It is regularly
not the one you stand on. The **patch set that is current on the server**
matters because an older one is still fetchable. An older one silently
reviews a revision nobody looks at. The **status** matters since MERGED and
ABANDONED are both answers that end the work. The **commit** says afterwards
whether the checkout holds the revision under review.
3. `typo3_rule_lookup` for the Gerrit workflow. It carries the ref you fetch a
patch set by. That is the one thing about a core change fetch nobody can
guess. It says which remote the ref is on, which is not the one the checkout
fetches from. It says the form the third way in below takes. It says what a
patch set opened on somebody else's change owes its author.
Those are sections of one page, and a search returns the one your words
matched. So read it whole where the fetch is the task, and always on the
fourth way in. That is `typo3_rule_lookup` with
`documentId="core/contribution/gerrit-workflow"`, which also stands as
`typo3://guides/core/contribution/gerrit-workflow`.
## Four ways in
The patch goes onto the branch it targets in the checkout you stand in, or into
a worktree beside it. Or it goes onto current code as a commit of your own. Or
it goes under work of your own, as the base you carry it on. The request usually
says which.
The first two hold the patch as its author wrote it. Whether the checkout is
free decides between them. The branch path needs it to itself. A worktree leaves
the current branch and everything uncommitted on it alone.
A worktree does not save that work, it moves it. It starts without the installed
dependencies, which git ignores and so does not bring. So no suite runs in it
until you install them there.
`typo3_test_run_guide` states that precondition above its suites. Beside it
stands the one check whose file list comes from git. In a worktree it reports
success after it read nothing. Read both before you run anything in one.
The third answers a different question: whether the change still applies to
current code and still passes there. "Cherry-pick it onto main" asks for it. It
takes the patch off the code its author wrote it on. So what the checkout then
holds is a commit this session made, not the revision under review.
That commit gets a name, `review/<change number>`, because the rest of the work
reads it. A reader can tell a named branch from the checkout's own work. A
reader can find it again after anything moves, and remove it on purpose. Where
the checkout is not free, this way in takes the worktree as the second does. You
create the branch there.
The fourth is the one the other three read as an obstacle. The patch is the
base, and work of your own goes on top of it. "Extend their patch with ours",
"amend somebody else's change" and "add this to the change" all ask for it. What
comes out is a further patch set on that change rather than a change of yours.
It uses the third's branch, started at the fetched patch set instead of at
current code. The section below says what it owes before you commit anything.
## One change, or a chain of them
`typo3_gerrit_lookup` answers a `chain` with every change read by name. A chain
longer than one open link changes the whole of what follows. A large refactoring
arrives this way, and the request says so. "Rebase the chain" names it outright.
Read the chain before the fetch and take four things off it.
- **Which links are still open.** You have to carry those, and their number is
how many commits the move is. The merged ones below them are already on the
branch, or should be.
- **Whether the merged link below is an ancestor.** `chainedAt` against
`patchSet` on that link says it. A link that shows `chainedAt` 18 and
`patchSet` 23 merged as a revision the chain never sat on. So
`git merge-base --is-ancestor` answers no, and a share of the conflicts below
follow from that one difference. Nothing in a checkout says it.
- **The move is a range.** Where one change is `git cherry-pick FETCH_HEAD`, a
chain is `git cherry-pick BASE..TIP`. `git rebase --onto origin/main BASE TIP`
is the same thing. The branch keeps the `review/<change number>` convention.
It takes the number of the **tip** change, which is the one the request named.
- **Every later amend is two steps.** You cannot amend a conflict resolution or
a fixer finding that belongs to the lower commit from the tip. Detach to it,
amend, apply again what sat on top, and move the branch. Budget for it rather
than discover it at the end.
The stopping rules below are for somebody else's patch, and a chain is regularly
the requester's own. The answer's owner field settles whether the change owner
is the person who asks. Where they are, the rule that stops you from an absent
author's decisions has nothing left to protect. To resolve past a handful of
hunks is the right call. Say in the result that you did, and on whose change.
## Before you change the checkout
Establish these three, in this order, and stop at the first that fails. The
first and the third are about the working copy the patch goes into. On the
worktree path that is the worktree.
- **The working tree is clean.** Afterwards nobody can tell uncommitted work and
a fetched patch set on top of each other apart. What a carry does to the
mixture is not recoverable from the checkout alone. Stop and say what the
uncommitted work is. Do not stash it as a convenience.
Where that uncommitted work is what stands in the way, the worktree is the way
past it.
On the fourth way in it is the material instead. Commit it where it stands,
before you fetch anything. Say in the answer which branch and which commit
that is. Then it has a base, which is what the file by file rule below reads
against.
- **The target branch is there and current.** A change that targets a release
branch, carried onto the wrong one, produces conflicts. Those are an artefact
of the mistake, and they look exactly like a stale patch.
- **Where you are is where you can get back to.** Write down the commit the
checkout is on before anything moves it. A worktree moves nothing, so what you
write down there is where the worktree is. To remove it is the whole of the
undo.
## Fetch and apply
Fetch the patch set the server says is current, and put the checkout on it. It
is somebody else's commit. It belongs on no local branch of yours for as long as
it is still that commit. The detached checkout and the worktree say that.
The fetch is the same on every path, and only its destination differs. That is
the checkout detached onto the commit, or a worktree on it while your branch
stays where it is. Or it is the branch named for the change with the commit
carried onto it. Or it is that same branch started at the commit itself.
Then establish what you hold before you judge anything about it. The checkout's
commit is the change's current revision, or it is not, and only the second needs
an explanation. A carry onto current code is the second by construction. The
section below says what you have to say instead.
## Carry it onto current code only where the work needs it
Carry the patch when the change does not sit on current code and the work needs
it to. That is when you run the suites or read it against code that has since
changed. It is also when the user asked for the carry. Not as a matter of
course. A patch read against the code its author wrote it on is the patch its
author wrote. A move is a step that can go wrong.
A rebase of the fetched commit and a cherry-pick onto current code are one move
under two names. A core patch is exactly one commit. A chain is the range above.
Either way, what comes out is a commit made here that exists nowhere else. That
is why it sits on a branch named for the change, and why the undo below deletes
that branch. The command form belongs to the page the fetch is on.
Where it applies clean, say so, and only that. A clean rebase is not a patch
that still holds. Where the change rewrites, moves or deletes a class, the
rebase reverts a fix `main` landed there. There is no conflict to show for it.
The files are different ones.
`typo3_rule_lookup` with `documentId="core/contribution/rebasing-a-stale-patch"`
holds the four steps that settle it. You owe them before the result says the
patch survived.
Say which commit every result after this is about. The carried commit's hash is
not the patch set's. A finding that quotes the local one without a word about
that names a revision nobody else can look up.
Where it conflicts, [references/checklist.md](references/checklist.md) decides
whether you resolve or stop, one conflict at a time. Read it at the first
conflict rather than after you resolved a few. The rule it carries is about what
you may know, and you cannot apply it backwards.
## Carry your own work onto the patch set
The ask comes first, and the author answers it. To extend a change under review
is ordinary practice, and you ask before you do it. So a session that does not
know whether somebody made the ask makes it rather than assumes it.
Where the ask has no answer, the answer is a comment on the change. The same
holds where what you would change is the author's own decision rather than a
correction. This workflow then stops with what it found. That is a result and
not a failure to deliver.
The page step 3 reads whole says the rest. It says what the amend does to the
author and to the committer. It says what the upload owes the author on the
change itself. It says what stays fixed whoever pushes. Read it before the first
commit and not before the push. The ask is not a step you can take afterwards.
Then the work moves one file at a time, and the rule is the one that already
decides a conflict. The checklist carries both halves. It says whether a file of
yours can go onto the patch whole. It says whether a hunk where the two collide
is yours to write.
Say what came from where. Say the patch set the branch started at and the commit
your own work was on. Say which files came from which side. The author will read
the diff between two patch sets, and that diff says what moved and never why.
**Once the result stands, invoke `typo3-core-patch-development`.** The push
belongs to that workflow. The amend, the message and the question of what goes
up visible to everyone are its steps. What crosses over is the change number,
the patch set the branch started at, and the result's branch. It is every
decision you took on the author's behalf. That last one is what the comment on
the change has to carry.
## Stopping is the normal ending
When a rule above ends the work, undo what you started rather than leave the
checkout half-way. The section below is that undo, and it is the same one that
ends a run that went fine. A checkout left half-way through a carry is a trap
for whoever opens it next, you included.
Report what you found and not what you attempted. Report the change, its patch
set, its target branch, how far it got, and the specific thing that stopped it.
That is the files that conflicted, the hunks, and why the change alone did not
decide them. That report is the useful outcome. Somebody who rebases the patch
properly starts from it.
## Once it is in and applies
`typo3_test_run_guide` with the paths the change touches names the suites that
can fail on it and their targeted invocations. Run them through the checkout's
own runner. A suite run through an installed binary is a result nobody can
reproduce. A check that inspected no files is not a green. The second is what a
worktree does by default. The same answer says what you have to install there
before any suite runs at all.
Say which branch and which patch set every result is about. Say which working
copy it ran in where that was a worktree, and which commit where you carried the
patch. A green reported without them has no owner the moment somebody pushes a
new patch set.
## Put the checkout back
A checkout on somebody's patch set is not a state to leave behind. It is not a
state to start the next piece of work from either. To restore it is a step of
its own, whether the patch applied or stopped. On the branch path it is these
six, in this order, because each part makes the next one possible.
There is one ending it does not run for, and it is the handover above. The work
continues on the review branch. So steps 2, 3, 5 and 6 would move the checkout
off the result or delete it.
What that ending owes is steps 1 and 4 and then the answer. That is the branch,
the commit, and what on it is not the author's. It is that you left the checkout
there on purpose. Neither "restored" nor silence is what such a session reports.
1. **End whatever is in progress first.** A carry that is half applied, or one
that stopped in a conflict, owns the tree until you abort it. Every later
step fails against it in a way that reads like something else.
2. **Return to the branch you recorded at the start**, not to whichever branch
looks right. Where you only fetched the patch, it was on no branch. So to
leave it loses nothing that is not still on the review server. Say the commit
in the answer, because that is the only local name it had.
3. **Delete the branch you carried the patch onto**, where there was one. Git
refuses the ordinary deletion, and the forced one gets past that refusal
rather than answers it. `typo3_gerrit_lookup` with `commit` and the tip the
branch is on answers it. A commit somebody pushed comes back as the change it
is a patch set of, superseded patch sets included. The ref beside it is the
undo, and you say it in the answer before the branch goes.
An empty answer is a commit nobody pushed, or one an anonymous reader may not
see. Nothing brings that one back. A carried commit is the second kind by
construction. So read what is about to go: what you resolved in a conflict is
in it and in nothing else. Ask the same of any other ref this workflow has to
remove. Nothing says in advance which of the two it is.
Then say in the answer that the branch is gone. "The checkout is back on its
branch" is true with the review branch still beside it.
4. **Establish that nothing of the patch remains.** An aborted carry can leave
files the change added untracked. They belong to no commit and to no branch.
The next suite run picks them up and fails for a reason that has nothing to
do with anything. What the working tree holds and what git does not track are
two different questions, and you ask both.
5. **Update the branch from the remote it fetches from, not from the review
server.** These are two different URLs on a core clone. `typo3_rule_lookup`
for the Gerrit workflow says which is which. The change refs live on only one
of them. Take the update as a fast-forward. A merge commit on a local branch
that tracks the core is a state nothing here asked for.
6. **Bring the installed dependencies back in step with the branch.** A move
between a patch set and current code can change what the lock file pins. A
suite run against the other revision's dependencies fails for a reason that
is not in the diff. This is the step people skip and then spend an hour on as
a test failure.
The worktree path ends shorter, and the difference is not a shortcut. Nothing
moved the branch, so steps 2, 5 and 6 have nothing to put back. They are about a
checkout that went somewhere.
What remains is to end whatever is in progress and then remove the worktree. Git
refuses that while anything in it has a modification or no tracking. That
refusal is step 4 one layer out. To force past it throws away the only copy of
whatever you resolved in there. Step 3 stands there too, because a removed
worktree leaves the branch you created it on behind.
Say the end state in the answer: which branch, which commit, and that the tree
is clean. On the worktree path, say that the worktree is gone. Or say where it
still is and why you kept it on purpose. Where you carried the patch, say that
you deleted the branch you carried it on. "Restored" without those is the claim
rather than the result.
This skill owns how a change under review gets into a checkout and back out of
it. What that covers, in order:
- Find it, and fetch the patch set.
- Put it on the branch it targets, into a worktree beside it, or onto current
code on a named branch. Or put it under work of your own where you extend that
change.
- Resolve what the change itself decides, and stop where it does not.
- Leave behind a clean branch current with its remote, no worktree of its own
and no branch it made. It owns the undo as much as the do, and the undo runs
whichever way the rest went. The one exception is the branch it hands over,
which is the result.
It does not own the judgement of the patch. Where the request is to say what is
wrong with it, `typo3-core-patch-review` owns that. That skill starts from the
working copy this one leaves the patch in, before the undo runs. That is the
worktree, the checkout or the review branch. Carry across which commit the
findings will be about.
It does not own a change to what is on the review server either. To amend a
change into a new patch set and push it belongs to
`typo3-core-patch-development`. Carry over the change number and the patch set
you fetched. Carry over whether you had to carry it onto current code to apply.
Carry over what you decided on the author's behalf.
References#
Where every task starts#
# Where every task starts
## Nothing starts until the server answers
A skill is a file the installer left behind. It loads and reads the same whether
the tools behind it are there or not, and neither side notices. So the first
call below is also the check.
- A client may carry this server's name in each tool's name:
`mcp__<server>__typo3_project_describe`. So a search for the bare name comes
back empty where the server is there. A search for a tool's schema needs the
same form. A `select:` on the bare names returns nothing where the tools are
there. Look for the qualified form before you read an empty result as an
answer about the server.
- No `typo3_` tool in this session, or a first call that errors: stop. Say that
this workflow needs the server and it is not there, and name what came back.
- Do not fall back to general TYPO3 knowledge, and do not start to read the
checkout. That answer carries this workflow's order and confidence and none of
its evidence. Nothing in it says which of the two it is.
- Continue only when the user asks you to after you said so. Repeat it in the
answer and in every finding a lookup would have carried.
## The order
This is an order rather than a list. Each step decides what the next one is
worth. Where a step below carries a condition to skip it, that condition is
narrow on purpose. A skipped prescription teaches the next reader to skip the
ones that matter too.
1. **`typo3_project_describe`** — the repository and whether it holds an
installation yet. It reports the TYPO3 and PHP version, the project's own
extensions, its sites, and the commands this repository declares. That
version filters every later answer. A check the repository does not declare
is a wrong answer however sensible it sounds.
The answer ends with the whole procedures this server carries, as ids. That
list is the only place a client that renders no resource list sees their
names. Each one is a `typo3_rule_lookup` with that `documentId` rather than a
search.
2. **`typo3_extension_describe`** for each extension in scope. It says what the
extension registers, and what it ships beside that. That is its manual, its
README, its test layers, and its XLF files with the source language each one
declares. It also says what the extension does *not* ship, and that is the
half no file listing gives you.
Where step 1 reported no extension, that answer is this step, and there is
nothing to call. Say so. A core checkout is that case, because step 1 names
the project's own extensions and not TYPO3's.
3. **`typo3_task_guide`** with a short English task, the paths it touches, the
target version and the change type. It answers the workflow this task belongs
to and the checks that come with it.
Run it in every session, this skill's own tasks included. The guide builds
the brief from the paths as well as the task text. No skill knows which paths
the caller holds.
A skill that covers the task is not that brief. A skipped step costs the
hints and the core checks those paths match. Where the guide's own answer
named this skill, this is one call for an answer already in the session. The
price of a step there is nothing to decide about.
4. **`typo3_hint_lookup`** for each subsystem in scope, with its concrete paths.
One query per subsystem. A single broad query is not subsystem evidence.
Where step 3 ran with those paths, its answer says whether you still owe this
step. A brief that carried everything the lookup matched says so: "these are
everything typo3_hint_lookup matches for these paths". There the guide made
the call, and the same query returns the same hints. A brief that stopped
short says that instead and names the ids it left. You owe those: fetch them
by id rather than repeat the query.
Read the sentence rather than the populated `hints` key. That key is present
either way and does not tell the two apart. `omittedHints` is that sentence
as data. It is empty where the brief carried everything, and it holds the ids
the brief left where it stopped short.
5. **`typo3_changelog_lookup` with `type: deprecation`**, at each major the
package declares. Omit the query and raise `limit` to carry that major whole.
Those two are the changelog's own axes, and the extension's vocabulary is not
among them. An entry carries a query only when its title carries every word
of it at once. The core titled those entries about its own code.
That is one call per declared major, and what comes back is the major. Every
entry carries its own index tags. `ext:core`, `ext:frontend`, `ext:form` and
the rest name the system extension a change is **in**. `TCA`, `TypoScript`,
`Fluid`, `YAML`, `Backend`, `Frontend` name the surface.
Step 2 picks the package's entries out of that answer by those tags. The tags
are the system extensions it requires, renders through or registers into, and
the kinds of file it ships. That costs no further call. An extension key of
your own is not among them and matches nothing. `tag` narrows one question
inside a major rather than composes the sweep out of eleven.
You check the answers against step 2, which is the other half the words did.
Verify each identifier that comes back in the checkout. A deprecation nothing
here calls is not a finding.
Carry the `FullyScanned` / `PartiallyScanned` tag into the answer. It says
whether the Extension Scanner can find the remaining call sites or whether
that reading is yours. Bounded this way, you can write the sweep before you
open a file. That is why it is a step of the order rather than something the
reading stumbles into.
**What its silence is worth.** A changelog records change events. So a
pattern nothing has touched for ten majors has no entry at all. An empty
sweep is therefore not an answer about what still works. "Does this still
work in version N" goes to `typo3_documentation_lookup` at that version. Ask
it here, and whenever the reading raises it again.
That is a question for a documented surface: a ViewHelper, a TCA type, a
TypoScript setting. The manual matches page titles, section paths and what
each manual declares by name, never the text of a page. Declared is a
property, a class or method the manual documents, a console command. You
reach one by its own name where the query writes that name the way code
does. You also reach it where the query is nothing but the name. A PHP
identifier the manual does not declare has no page named after it.
An identifier goes to `typo3_changelog_lookup` under its own name. That
reaches the entries that write it, however the core titled the change. Then
it goes to the class below. Where the manual has no page for a surface
either, that is a result and not an answer. Undocumented is not unsupported.
**A second declared major.** A package that declares more than one asks a
second question of every deprecation the sweep returns. Is the replacement on
the lower one? The entry's `issue` is a query of its own, and it reaches
every entry filed under that number. The Feature the core announced the
replacement in is among them.
The version the core released it in settles that question. Where the number
reaches no sibling, nobody wrote an entry for the replacement.
`typo3_rule_lookup` with
`documentId="extension/compatibility/a-declared-major-that-is-not-installed"`
is the reading that closes it.
**Where you do not owe the sweep.** A task that produces no change does not
reach this step at all. The property is what the task produces. A triage, a
reproduction and a review illustrate it; they are not the list you read it
off. The sweep asks what a package will have to stop calling. A task that
writes nothing is not going to call anything.
The exemption ends where the workflow produces a change. A review asked to
make the change is that other workflow. It starts this order again with the
files it is about to write. To carry somebody else's patch onto current code
is on the same side. It writes commits. The sweep says whether the code that
moved under the patch deprecated something the patch calls.
Skip the sweep only where the change touches no TYPO3 API: a code style
fixer, a CI file, an `.editorconfig`. A deprecation is a statement about API
the package calls. So a change that calls none has nothing for the sweep to
land on. The sweep is empty before it runs.
That condition is worth a statement, because this step is the largest answer
the order asks for. It is one call per declared major, with that major's
deprecations whole. You read which side a change falls on off the files it
touches, never off the task it started as. One PHP file edited along the way
puts it back among the ordinary ones.
A skip there costs the deprecation no finding would have walked into. How
small the change is decides nothing either. Three statements can call a
deprecated API as easily as three hundred.
A test file is one of those wherever it sits. It calls the API it exercises
and the framework around it, and both deprecate. A fixture is exempt where it
is data the suite reads, and not where it is a class.
**Before the reading**, write down what the order established. That is the
version that filters every later answer, the packages in scope, and the commands
this repository declares. Write down which steps what discharged. Those are
answers already in the session rather than a second reading. What the files show
belongs to the report at the other end. A caller who cannot see what an answer
rests on cannot tell it from one that rests on nothing.
**Then** read the checkout. Not before. A file list first makes everything after
the list look optional. The conventions then arrive as a footnote to a verdict
that has already formed.
**Before the first edit**, name the files this change will create, change or
delete. A deletion is the caller's to ask for, and this is somebody else's
checkout. It is the one act nothing here can put back.
**Last**, the report names every step of this order it did not reach, and what
stood in for it. That is an answer already in the session, a condition that made
the step empty, or an exemption. A reader cannot tell a step passed over in
silence from one somebody dropped.
## When the lookups run out
A behaviour question that survives the lookups above is one you read out of the
installed source. Do not guess at it. The class that implements the behaviour
and the one it inherits from answer it. That reading is the step after the
lookups. It replaces a change to the code until it works.
A first change that did not work is evidence about the reading. So the second
attempt at one failure reads the source rather than changes the code again.
What it settles is what this installation does and never what TYPO3 supports. So
a finding says that you could not settle the question beyond the version
installed. An answer built on the reading names the version it holds for.
## What each runtime lookup adds after the extension answer
`typo3_extension_describe` in step 2 says what one package registers. The
lookups below say what the installation resolved. That is a different fact even
where the words are the same. So step 2 has made none of these calls:
- `typo3_backend_module_lookup` — the tree position, the labels, the access
level, the routes and the navigation component the parent module supplies.
Step 2 lists the modules the package declares. A declaration cannot show that
inheritance.
- `typo3_icon_lookup` — whether any installed package registers an identifier.
That validates the ones a template uses. Step 2 lists the identifiers this
package contributes.
- `typo3_label_lookup` — the labels as the installation resolves them, with its
overrides applied. Step 2 lists the package's XLF files and the source
language each declares, never what a unit says here.
- `typo3_fluid_namespace_list` — the prefixes any template may use without a
declaration, from every package at once. Step 2 lists the package's own
declarations. So an empty list there is no evidence that no package registers
a prefix globally.
- `typo3_configuration_lookup` — the resolved configuration value, after every
extension has had its say. For a form data group it gives the order the
providers really run in. Step 2 answers nothing about that surface at all.
What a registration declares is not what the installation resolves.
- `typo3_service_lookup` — the class the container really injects for a service
id, an interface or a tag. Decorations and overrides count. Step 2 lists what
the package's own `Services.yaml` declares, never what won.
- `typo3_schema_lookup` — the columns TYPO3 derives for a table from its TCA. It
gives the type, the nullability and the default each one gets. Step 2 lists
the tables the package registers and nothing about their shape.
- `typo3_flexform_lookup` — the data structure the installation resolves a
`type=flex` field to, sheet by sheet, with listeners and migrations applied.
Step 2 lists the content elements a package registers, never the structure
each one's form builds.
- `typo3_record_lookup` — the rows of any table the installation has TCA for. It
says how many there are and where they sit. It says what one column holds
across them and which rows depart from its default. Step 2 has no row in its
answer at all.
None of these says whether what it reports is right. `typo3_hint_lookup` and
`typo3_documentation_lookup` do. A subsystem its own runtime lookup confirmed
can still break every rule that governs it. So it is not established until you
asked both.
## A rule reads in both directions
It says what new code should do, and it says what this checkout already does
wrong. A file that settled into the opposite of a rule is a finding, not a local
style to preserve. Consistency with a project's own habit establishes nothing
about whether the habit is right.
## What the code is for is evidence, and the repository states it
A mechanism that costs something is not a defect because it costs. Before you
report one, find what it is there for and say so. That is the manual, the
README, the changelog, the setting that drives it, or the declared versions.
Where the documentation states a purpose, what you have is a trade-off, not a
defect. Name it with its cost and its alternative. Where you cannot find one,
the finding says that you could not establish one, not that none exists. If you
skip this, your review is a list of everything the author did on purpose.
## What a finding rests on is part of the finding
Three things carry one. A file you read, at its path and its line. A command you
ran, with what it printed. A mechanism you traced into an installed package. Say
which of the three it is. If you leave it unsaid, a finding from a CI file
weighs as much as one with a verified line.
Where one of the project's own commands would settle it, run it.
`typo3_project_describe` marks each command it lists **check**, **change** or
**unknown**, read off the declared body. A check reports and hands the code back
as it was. So even a task told not to change files runs it. The linter the
repository already declares is the cheapest evidence in it.
Do not run a change under that instruction. Name an unknown in the answer as
evidence that is available, and do not run it unasked. An unknown is a test
suite, a shell pipeline, a console command.
What a check prints is not the finding. The configuration that makes it fail is
still what the finding is about. The run takes that finding from derived to
established.
## What this server does not know
It does not read your working tree. You establish which files changed, which
branch you are on, and whether a path or an identifier still exists there. Then
pass the concrete paths back, because that turns a general convention into an
answer about this code.
## Query it in English
The knowledge is English and the match is lexical. So a query in another
language reaches the loanwords the two happen to share and nothing else.
Translate the subject before the call and the answer back afterwards, whatever
language you speak with the user.
markdown
# Where every task starts
## Nothing starts until the server answers
A skill is a file the installer left behind. It loads and reads the same whether
the tools behind it are there or not, and neither side notices. So the first
call below is also the check.
- A client may carry this server's name in each tool's name:
`mcp__<server>__typo3_project_describe`. So a search for the bare name comes
back empty where the server is there. A search for a tool's schema needs the
same form. A `select:` on the bare names returns nothing where the tools are
there. Look for the qualified form before you read an empty result as an
answer about the server.
- No `typo3_` tool in this session, or a first call that errors: stop. Say that
this workflow needs the server and it is not there, and name what came back.
- Do not fall back to general TYPO3 knowledge, and do not start to read the
checkout. That answer carries this workflow's order and confidence and none of
its evidence. Nothing in it says which of the two it is.
- Continue only when the user asks you to after you said so. Repeat it in the
answer and in every finding a lookup would have carried.
## The order
This is an order rather than a list. Each step decides what the next one is
worth. Where a step below carries a condition to skip it, that condition is
narrow on purpose. A skipped prescription teaches the next reader to skip the
ones that matter too.
1. **`typo3_project_describe`** — the repository and whether it holds an
installation yet. It reports the TYPO3 and PHP version, the project's own
extensions, its sites, and the commands this repository declares. That
version filters every later answer. A check the repository does not declare
is a wrong answer however sensible it sounds.
The answer ends with the whole procedures this server carries, as ids. That
list is the only place a client that renders no resource list sees their
names. Each one is a `typo3_rule_lookup` with that `documentId` rather than a
search.
2. **`typo3_extension_describe`** for each extension in scope. It says what the
extension registers, and what it ships beside that. That is its manual, its
README, its test layers, and its XLF files with the source language each one
declares. It also says what the extension does *not* ship, and that is the
half no file listing gives you.
Where step 1 reported no extension, that answer is this step, and there is
nothing to call. Say so. A core checkout is that case, because step 1 names
the project's own extensions and not TYPO3's.
3. **`typo3_task_guide`** with a short English task, the paths it touches, the
target version and the change type. It answers the workflow this task belongs
to and the checks that come with it.
Run it in every session, this skill's own tasks included. The guide builds
the brief from the paths as well as the task text. No skill knows which paths
the caller holds.
A skill that covers the task is not that brief. A skipped step costs the
hints and the core checks those paths match. Where the guide's own answer
named this skill, this is one call for an answer already in the session. The
price of a step there is nothing to decide about.
4. **`typo3_hint_lookup`** for each subsystem in scope, with its concrete paths.
One query per subsystem. A single broad query is not subsystem evidence.
Where step 3 ran with those paths, its answer says whether you still owe this
step. A brief that carried everything the lookup matched says so: "these are
everything typo3_hint_lookup matches for these paths". There the guide made
the call, and the same query returns the same hints. A brief that stopped
short says that instead and names the ids it left. You owe those: fetch them
by id rather than repeat the query.
Read the sentence rather than the populated `hints` key. That key is present
either way and does not tell the two apart. `omittedHints` is that sentence
as data. It is empty where the brief carried everything, and it holds the ids
the brief left where it stopped short.
5. **`typo3_changelog_lookup` with `type: deprecation`**, at each major the
package declares. Omit the query and raise `limit` to carry that major whole.
Those two are the changelog's own axes, and the extension's vocabulary is not
among them. An entry carries a query only when its title carries every word
of it at once. The core titled those entries about its own code.
That is one call per declared major, and what comes back is the major. Every
entry carries its own index tags. `ext:core`, `ext:frontend`, `ext:form` and
the rest name the system extension a change is **in**. `TCA`, `TypoScript`,
`Fluid`, `YAML`, `Backend`, `Frontend` name the surface.
Step 2 picks the package's entries out of that answer by those tags. The tags
are the system extensions it requires, renders through or registers into, and
the kinds of file it ships. That costs no further call. An extension key of
your own is not among them and matches nothing. `tag` narrows one question
inside a major rather than composes the sweep out of eleven.
You check the answers against step 2, which is the other half the words did.
Verify each identifier that comes back in the checkout. A deprecation nothing
here calls is not a finding.
Carry the `FullyScanned` / `PartiallyScanned` tag into the answer. It says
whether the Extension Scanner can find the remaining call sites or whether
that reading is yours. Bounded this way, you can write the sweep before you
open a file. That is why it is a step of the order rather than something the
reading stumbles into.
**What its silence is worth.** A changelog records change events. So a
pattern nothing has touched for ten majors has no entry at all. An empty
sweep is therefore not an answer about what still works. "Does this still
work in version N" goes to `typo3_documentation_lookup` at that version. Ask
it here, and whenever the reading raises it again.
That is a question for a documented surface: a ViewHelper, a TCA type, a
TypoScript setting. The manual matches page titles, section paths and what
each manual declares by name, never the text of a page. Declared is a
property, a class or method the manual documents, a console command. You
reach one by its own name where the query writes that name the way code
does. You also reach it where the query is nothing but the name. A PHP
identifier the manual does not declare has no page named after it.
An identifier goes to `typo3_changelog_lookup` under its own name. That
reaches the entries that write it, however the core titled the change. Then
it goes to the class below. Where the manual has no page for a surface
either, that is a result and not an answer. Undocumented is not unsupported.
**A second declared major.** A package that declares more than one asks a
second question of every deprecation the sweep returns. Is the replacement on
the lower one? The entry's `issue` is a query of its own, and it reaches
every entry filed under that number. The Feature the core announced the
replacement in is among them.
The version the core released it in settles that question. Where the number
reaches no sibling, nobody wrote an entry for the replacement.
`typo3_rule_lookup` with
`documentId="extension/compatibility/a-declared-major-that-is-not-installed"`
is the reading that closes it.
**Where you do not owe the sweep.** A task that produces no change does not
reach this step at all. The property is what the task produces. A triage, a
reproduction and a review illustrate it; they are not the list you read it
off. The sweep asks what a package will have to stop calling. A task that
writes nothing is not going to call anything.
The exemption ends where the workflow produces a change. A review asked to
make the change is that other workflow. It starts this order again with the
files it is about to write. To carry somebody else's patch onto current code
is on the same side. It writes commits. The sweep says whether the code that
moved under the patch deprecated something the patch calls.
Skip the sweep only where the change touches no TYPO3 API: a code style
fixer, a CI file, an `.editorconfig`. A deprecation is a statement about API
the package calls. So a change that calls none has nothing for the sweep to
land on. The sweep is empty before it runs.
That condition is worth a statement, because this step is the largest answer
the order asks for. It is one call per declared major, with that major's
deprecations whole. You read which side a change falls on off the files it
touches, never off the task it started as. One PHP file edited along the way
puts it back among the ordinary ones.
A skip there costs the deprecation no finding would have walked into. How
small the change is decides nothing either. Three statements can call a
deprecated API as easily as three hundred.
A test file is one of those wherever it sits. It calls the API it exercises
and the framework around it, and both deprecate. A fixture is exempt where it
is data the suite reads, and not where it is a class.
**Before the reading**, write down what the order established. That is the
version that filters every later answer, the packages in scope, and the commands
this repository declares. Write down which steps what discharged. Those are
answers already in the session rather than a second reading. What the files show
belongs to the report at the other end. A caller who cannot see what an answer
rests on cannot tell it from one that rests on nothing.
**Then** read the checkout. Not before. A file list first makes everything after
the list look optional. The conventions then arrive as a footnote to a verdict
that has already formed.
**Before the first edit**, name the files this change will create, change or
delete. A deletion is the caller's to ask for, and this is somebody else's
checkout. It is the one act nothing here can put back.
**Last**, the report names every step of this order it did not reach, and what
stood in for it. That is an answer already in the session, a condition that made
the step empty, or an exemption. A reader cannot tell a step passed over in
silence from one somebody dropped.
## When the lookups run out
A behaviour question that survives the lookups above is one you read out of the
installed source. Do not guess at it. The class that implements the behaviour
and the one it inherits from answer it. That reading is the step after the
lookups. It replaces a change to the code until it works.
A first change that did not work is evidence about the reading. So the second
attempt at one failure reads the source rather than changes the code again.
What it settles is what this installation does and never what TYPO3 supports. So
a finding says that you could not settle the question beyond the version
installed. An answer built on the reading names the version it holds for.
## What each runtime lookup adds after the extension answer
`typo3_extension_describe` in step 2 says what one package registers. The
lookups below say what the installation resolved. That is a different fact even
where the words are the same. So step 2 has made none of these calls:
- `typo3_backend_module_lookup` — the tree position, the labels, the access
level, the routes and the navigation component the parent module supplies.
Step 2 lists the modules the package declares. A declaration cannot show that
inheritance.
- `typo3_icon_lookup` — whether any installed package registers an identifier.
That validates the ones a template uses. Step 2 lists the identifiers this
package contributes.
- `typo3_label_lookup` — the labels as the installation resolves them, with its
overrides applied. Step 2 lists the package's XLF files and the source
language each declares, never what a unit says here.
- `typo3_fluid_namespace_list` — the prefixes any template may use without a
declaration, from every package at once. Step 2 lists the package's own
declarations. So an empty list there is no evidence that no package registers
a prefix globally.
- `typo3_configuration_lookup` — the resolved configuration value, after every
extension has had its say. For a form data group it gives the order the
providers really run in. Step 2 answers nothing about that surface at all.
What a registration declares is not what the installation resolves.
- `typo3_service_lookup` — the class the container really injects for a service
id, an interface or a tag. Decorations and overrides count. Step 2 lists what
the package's own `Services.yaml` declares, never what won.
- `typo3_schema_lookup` — the columns TYPO3 derives for a table from its TCA. It
gives the type, the nullability and the default each one gets. Step 2 lists
the tables the package registers and nothing about their shape.
- `typo3_flexform_lookup` — the data structure the installation resolves a
`type=flex` field to, sheet by sheet, with listeners and migrations applied.
Step 2 lists the content elements a package registers, never the structure
each one's form builds.
- `typo3_record_lookup` — the rows of any table the installation has TCA for. It
says how many there are and where they sit. It says what one column holds
across them and which rows depart from its default. Step 2 has no row in its
answer at all.
None of these says whether what it reports is right. `typo3_hint_lookup` and
`typo3_documentation_lookup` do. A subsystem its own runtime lookup confirmed
can still break every rule that governs it. So it is not established until you
asked both.
## A rule reads in both directions
It says what new code should do, and it says what this checkout already does
wrong. A file that settled into the opposite of a rule is a finding, not a local
style to preserve. Consistency with a project's own habit establishes nothing
about whether the habit is right.
## What the code is for is evidence, and the repository states it
A mechanism that costs something is not a defect because it costs. Before you
report one, find what it is there for and say so. That is the manual, the
README, the changelog, the setting that drives it, or the declared versions.
Where the documentation states a purpose, what you have is a trade-off, not a
defect. Name it with its cost and its alternative. Where you cannot find one,
the finding says that you could not establish one, not that none exists. If you
skip this, your review is a list of everything the author did on purpose.
## What a finding rests on is part of the finding
Three things carry one. A file you read, at its path and its line. A command you
ran, with what it printed. A mechanism you traced into an installed package. Say
which of the three it is. If you leave it unsaid, a finding from a CI file
weighs as much as one with a verified line.
Where one of the project's own commands would settle it, run it.
`typo3_project_describe` marks each command it lists **check**, **change** or
**unknown**, read off the declared body. A check reports and hands the code back
as it was. So even a task told not to change files runs it. The linter the
repository already declares is the cheapest evidence in it.
Do not run a change under that instruction. Name an unknown in the answer as
evidence that is available, and do not run it unasked. An unknown is a test
suite, a shell pipeline, a console command.
What a check prints is not the finding. The configuration that makes it fail is
still what the finding is about. The run takes that finding from derived to
established.
## What this server does not know
It does not read your working tree. You establish which files changed, which
branch you are on, and whether a path or an identifier still exists there. Then
pass the concrete paths back, because that turns a general convention into an
answer about this code.
## Query it in English
The knowledge is English and the match is lexical. So a query in another
language reaches the loanwords the two happen to share and nothing else.
Translate the subject before the call and the answer back afterwards, whatever
language you speak with the user.
Resolve or stop#
# Resolve or stop
One rule decides every conflict, and it is about what you may know:
**You can resolve a conflict only where the change itself decides it.** The
patch says what it wants, and the branch says what is there now. Where those two
together leave exactly one way to write the hunk, to write it is transcription.
Where they leave a choice, the choice is the author's. To take it produces a
patch nobody wrote. Others then read, run and report on it as if it were the
author's.
Apply it per conflict, not per file. A change can be transcription in one hunk
and somebody else's decision in the next. The second one ends the work whatever
happened in the first.
## Resolve
- **Context moved.** The lines the patch touches stay as they were; something
above or below them shifted. Nothing to decide.
- **A rename the branch made and the patch predates.** The change's intent stays
the same under the new name, and the branch uses the new name everywhere.
Verify the second before you believe the first.
- **Both sides made the same edit.** To keep one copy is the only reading.
- **Import, use-statement and namespace order.** The tool the checkout declares
decides these, not you. Run it and take what it writes.
## Stop
- **Both sides changed the same lines with different intent.** This is the case
the rule exists for. Any resolution is an authoring decision.
- **The branch already fixed what the patch fixes, differently.** The patch may
now be unnecessary, partly unnecessary, or a better approach that should
replace the other. All three are the author's and a reviewer's call.
- **The API the patch calls is gone or changed shape.** To rewrite a call site
to the new API is to write the patch. It is also the moment the branch might
no longer need the change at all.
- **The conflict is in a test's expectations.** What the expectation should be
is the substance of the change, never a merge artefact.
- **The conflict is in a changelog entry, a fixture or generated output.** Which
version it belongs to and what it says are decisions upstream of the diff.
- **You would have to read the issue again to decide it.** That is the signal
that the change no longer carries its own answer.
- **More than a handful of hunks conflict.** Resolvable one by one or not, the
patch is stale. A rebase by its author is the honest outcome. Say how many and
where.
## Taking your own work onto the patch
The same rule, applied to a whole file rather than to a hunk. Your work and the
patch have two different bases. Whether those two bases already agree about a
file decides whether a file of yours can go over wholesale.
- **The file is identical on both bases.** You overwrite nothing of the
author's, so a copy of yours over it is transcription. Establish that by a
comparison of the two bases on that path. A read of the file says nothing
about it.
- **The two bases differ on it.** Your version carries whatever moved on your
base as well as your own work. To take it whole reverts the author's side
without a word. Move your own hunks instead, and the rule above decides each
of them.
- **The patch itself touches the file.** A conflict from the start, whatever the
bases say, and never a transcription.
Say per file which of the three it was. The author has to be able to check that,
and the diff between two patch sets does not carry it.
## What a stop reports
- The change, its patch set, its target branch, and the commit you put the
checkout back on.
- Which files and which hunks conflicted, and for each one which stopping rule
it hit.
- What the two sides wanted, in one sentence each. That makes the report usable
by whoever rebases it properly, and it is the part a diff does not say.
- Whether you resolved anything before the stop, and what. A partly resolved
carry you then aborted still tells the next person which hunks are free.
## After any resolution
- The build and the suites that cover the touched paths say whether the
resolution holds. A carry that produced a checkout nobody ran is not a carry
that worked.
- Say that the checkout holds the patch on other code and is no longer the
revision under review. Every result from it is about the commit you made and
not about the patch set the reviewers see. To report one as the other is the
failure this whole file guards.
- The carried state is local. A push opens a further patch set on the change it
came from. The identifier that links the two travels with the commit. Where
the user asked for that, it is a normal move. The Gerrit workflow page says
what it owes its author.
The push itself belongs to the workflow that owns the amend of a change. Where
the user did not ask for it, the state stays here and the report is the
answer.
markdown
# Resolve or stop
One rule decides every conflict, and it is about what you may know:
**You can resolve a conflict only where the change itself decides it.** The
patch says what it wants, and the branch says what is there now. Where those two
together leave exactly one way to write the hunk, to write it is transcription.
Where they leave a choice, the choice is the author's. To take it produces a
patch nobody wrote. Others then read, run and report on it as if it were the
author's.
Apply it per conflict, not per file. A change can be transcription in one hunk
and somebody else's decision in the next. The second one ends the work whatever
happened in the first.
## Resolve
- **Context moved.** The lines the patch touches stay as they were; something
above or below them shifted. Nothing to decide.
- **A rename the branch made and the patch predates.** The change's intent stays
the same under the new name, and the branch uses the new name everywhere.
Verify the second before you believe the first.
- **Both sides made the same edit.** To keep one copy is the only reading.
- **Import, use-statement and namespace order.** The tool the checkout declares
decides these, not you. Run it and take what it writes.
## Stop
- **Both sides changed the same lines with different intent.** This is the case
the rule exists for. Any resolution is an authoring decision.
- **The branch already fixed what the patch fixes, differently.** The patch may
now be unnecessary, partly unnecessary, or a better approach that should
replace the other. All three are the author's and a reviewer's call.
- **The API the patch calls is gone or changed shape.** To rewrite a call site
to the new API is to write the patch. It is also the moment the branch might
no longer need the change at all.
- **The conflict is in a test's expectations.** What the expectation should be
is the substance of the change, never a merge artefact.
- **The conflict is in a changelog entry, a fixture or generated output.** Which
version it belongs to and what it says are decisions upstream of the diff.
- **You would have to read the issue again to decide it.** That is the signal
that the change no longer carries its own answer.
- **More than a handful of hunks conflict.** Resolvable one by one or not, the
patch is stale. A rebase by its author is the honest outcome. Say how many and
where.
## Taking your own work onto the patch
The same rule, applied to a whole file rather than to a hunk. Your work and the
patch have two different bases. Whether those two bases already agree about a
file decides whether a file of yours can go over wholesale.
- **The file is identical on both bases.** You overwrite nothing of the
author's, so a copy of yours over it is transcription. Establish that by a
comparison of the two bases on that path. A read of the file says nothing
about it.
- **The two bases differ on it.** Your version carries whatever moved on your
base as well as your own work. To take it whole reverts the author's side
without a word. Move your own hunks instead, and the rule above decides each
of them.
- **The patch itself touches the file.** A conflict from the start, whatever the
bases say, and never a transcription.
Say per file which of the three it was. The author has to be able to check that,
and the diff between two patch sets does not carry it.
## What a stop reports
- The change, its patch set, its target branch, and the commit you put the
checkout back on.
- Which files and which hunks conflicted, and for each one which stopping rule
it hit.
- What the two sides wanted, in one sentence each. That makes the report usable
by whoever rebases it properly, and it is the part a diff does not say.
- Whether you resolved anything before the stop, and what. A partly resolved
carry you then aborted still tells the next person which hunks are free.
## After any resolution
- The build and the suites that cover the touched paths say whether the
resolution holds. A carry that produced a checkout nobody ran is not a carry
that worked.
- Say that the checkout holds the patch on other code and is no longer the
revision under review. Every result from it is about the commit you made and
not about the patch set the reviewers see. To report one as the other is the
failure this whole file guards.
- The carried state is local. A push opens a further patch set on the change it
came from. The identifier that links the two travels with the commit. Where
the user asked for that, it is a normal move. The Gerrit workflow page says
what it owes its author.
The push itself belongs to the workflow that owns the amend of a change. Where
the user did not ask for it, the state stays here and the report is the
answer.