---
title: "TYPO3 Core Patch Checkout"
description: "Skill: typo3-core-patch-checkout"
canonical: index.html
navigation-title: "TYPO3 Core Patch Checkout"
---

<a id="typo3-core-patch-checkout"></a>

# TYPO3 Core Patch Checkout

- [Markdown source](#markdown-source)
- [References](#references)
  - [Where every task starts](#where-every-task-starts)
  - [Resolve or stop](#resolve-or-stop)

**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.

<a id="markdown-source"></a>

## Markdown source

```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.
```

<a id="references"></a>

## References

<a id="where-every-task-starts"></a>

### Where every task starts

```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.
```

<a id="resolve-or-stop"></a>

### Resolve or stop

```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.
```
