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, or cherry-picked onto current code on a review branch. Trying one out, checking whether it still applies, getting a checkout back onto a clean current branch. Rebasing your own commit 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, or cherry-picked onto current code on a review branch. Trying one out, checking whether it still applies, getting a checkout back onto a clean current branch. Rebasing your own commit 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 actually in.
Keep this skill as routing and stopping rules; the refs, the remotes and the
commands are lookups, and a copy of them here goes stale in somebody else's
checkout with nothing to report it.
The reason this is a workflow of its own is what it must not do. A patch that no
longer applies is a finding — the branch moved under a change nobody rebased —
and a session that quietly resolves its way to something that compiles has
destroyed that finding and produced a patch nobody wrote. So every step below
has an end, and reaching one is a result.
## Establish the change before touching 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, or with the
issue number where the change was reached through its issue. Four things it
answers decide everything below: the **branch the change targets**, which is
what it has to be applied onto and is regularly not the one you are standing
on; the **patch set that is current on the server**, because an older one is
still fetchable and applying it silently reviews a revision nobody is looking
at; the **status**, since MERGED and ABANDONED are both answers that end the
work; and the **commit**, which is what says afterwards whether the checkout
holds the revision under review.
3. `typo3_rule_lookup` for the Gerrit workflow. It carries the ref a patch set
is fetched by, the one thing about fetching a core change that is not
guessable — which remote the ref is on, which is not the one the checkout
fetches from — and the form the third way in below takes. Those are sections
of one page and a search returns the one your words matched, so where the
fetch is the task read it whole: `typo3_rule_lookup` with
`documentId="core/contribution/gerrit-workflow"`, which also stands as
`typo3://guides/core/contribution/gerrit-workflow`.
## Three ways in
The patch goes onto the branch it targets in the checkout you are standing in,
into a git worktree beside it, or onto current code as a commit of your own. The
request usually says which.
The first two hold the patch as its author wrote it, and what decides between
them is whether the checkout is free: the branch path needs it to itself, and 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 are gitignored and so are not something git brings, and no
suite runs in it until they are installed there. `typo3_test_run_guide` states
that precondition above its suites, and beside it the one check whose file list
comes from git, which reports success having read nothing in a worktree. Read
both before running anything in one.
The third answers a different question — whether the change still applies to
current code and still passes there — and it is what "cherry-pick it onto main"
asks for. It takes the patch off the code it was written on, so what the
checkout then holds is a commit this session made and not the revision under
review. That commit gets a name, `review/<change number>`, because the rest of
the work reads it: a named branch can be told apart from the checkout's own
work, found again after anything moves, and removed on purpose. Where the
checkout is not free, this way in takes the worktree as the second does, and the
branch is created there.
## Before the checkout is changed
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 is going into, which on
the worktree path is the worktree.
- **The working tree is clean.** Uncommitted work and a fetched patch set on top
of each other cannot be told apart afterwards, and what carrying it does to
the mixture is not recoverable from the checkout alone. Stop and say what is
uncommitted; do not stash it as a convenience. Where that uncommitted work is
what stands in the way, the worktree is the way past it.
- **The target branch is there and current.** A change targeting a release
branch carried onto the wrong one produces conflicts that are an artefact of
the mistake, and they look exactly like a stale patch.
- **Where you are is where you will be able to get back to.** Write down the
commit the checkout is on before anything moves it. A worktree moves nothing,
so what is written down there is where the worktree is: removing 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 and belongs on no local branch of yours for as long as
it is still that commit, which is what the detached checkout and the worktree
say. The fetch is the same on all three paths and only its destination differs:
the checkout detached onto the commit, a worktree created on it while the branch
you were on stays where it is, or the branch named for the change with the
commit carried onto it.
Then establish what you are holding before judging anything about it: the
checkout's commit is the change's current revision, or it is not, and only the
second needs explaining. Carrying it onto current code is the second by
construction, and the section below is what has to be said 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 — running the suites, reading it against code that has since changed, or
because carrying it is what was asked for. Not as a matter of course: a patch
read against the code it was written on is the patch its author wrote, and
moving it 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, because a core patch is exactly one commit. Either way what
comes out is a commit made here that exists nowhere else, which is why it is 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. That is itself an answer about the change: it
still applies.
Say which commit every result after this is about. The carried commit's hash is
not the patch set's, and a finding quoting the local one without saying so is
about a revision nobody else can look up.
Where it conflicts, [references/checklist.md](references/checklist.md) is what
decides whether to resolve or to stop, one conflict at a time. Read it at the
first conflict rather than after resolving a few — the rule it carries is about
what you are allowed to know, and it cannot be applied backwards.
## Stopping is the normal ending
When a rule above ends the work, undo what was started rather than leaving the
checkout half-way — the section below is that undo, and it is the same one that
ends a run which went fine. A checkout left half-way through a carry is a trap
for whoever opens it next, including you.
Report what was found and not what was attempted. The change, its patch set, its
target branch, how far it got, and the specific thing that stopped it — the
files that conflicted, the hunks, and why the change alone did not decide them.
That report is the useful outcome, and it is what somebody rebasing the patch
properly starts from.
## 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, and a check that inspected no files is not a green. The second is
what a worktree does by default, and what has to be installed there before any
suite runs at all is in the same answer.
Say which branch and which patch set every result is about, which working copy
it ran in where that was a worktree, and which commit where the patch was
carried. A green reported without them is unattributable the moment a new patch
set is pushed.
## Put the checkout back
A checkout sitting on somebody's patch set is not a state to leave behind, and
it is not a state to start the next piece of work from either. Restoring it is a
step of its own, taken 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.
1. **End whatever is in progress first.** A carry that is half applied, or one
that stopped in a conflict, owns the working tree until it is aborted, and
every later step fails against it in a way that reads like something else.
2. **Return to the branch that was recorded at the start**, not to whichever
branch looks right. Where the patch was only fetched, it was on no branch, so
leaving it loses nothing that is not still on the review server — but say the
commit in the answer, because that is the only local name it had.
3. **Delete the branch the patch was carried onto**, where there was one. That
commit is on no review server and in no other branch, so the branch is the
only thing holding it and git refuses the ordinary deletion: the forced one
is the step, and the refusal is the last moment anything asks whether it is
really disposable. Read what is about to go rather than reaching past it —
what was resolved in a conflict goes with it. Then say in the answer that the
branch is gone, because "the checkout is back on its branch" is true with the
review branch still sitting beside it.
4. **Establish that nothing of the patch is left.** An aborted carry can leave
files the change added lying untracked, and 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 is
untracked are two different questions and both have to be asked.
5. **Update the branch from the remote it is fetched 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 — and the change refs live on
only one of them. Take the update as a fast-forward: a merge commit on a
local branch tracking the core is a state nothing here asked for.
6. **Bring the installed dependencies back in step with the branch.** Moving
between a patch set and current code can change what the lock file pins, and
a suite run against dependencies belonging to the other revision fails for a
reason that is not in the diff. This is the step that is skipped and then
spends an hour being diagnosed 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 is left is ending whatever is in progress
and then removing the worktree, which is refused while anything in it is
modified or untracked: that refusal is step 4 arriving one layer out, and
forcing past it throws away the only copy of whatever was resolved in there.
Step 3 stands there too, because removing a worktree leaves the branch it was
created on behind.
Say the end state in the answer: which branch, which commit, and that the tree
is clean; on the worktree path, that the worktree is gone, or where it still is
and why keeping it was deliberate; and where the patch was carried, that the
branch it was carried on is deleted. "Restored" without those is the claim
rather than the result.
This skill owns getting a change under review into a checkout and back out of
it: finding it, fetching the patch set, putting it on the branch it targets,
into a worktree beside it, or onto current code on a branch named for the
change, resolving what the change itself decides, stopping where it does not,
and leaving 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 is
what runs whichever way the rest went. It does not own judging the patch — where
the request is to say what is wrong with it, `typo3-core-patch-review` owns
that, and it starts from the working copy this leaves the patch in, worktree,
checkout or review branch, before the undo runs; carry across which commit the
findings will be about. It does not own changing the patch either: amending a
change into a new patch set and pushing it belongs to
`typo3-core-patch-development`, and carry over the change number, the patch set
that was fetched and whether it had to be carried onto current code to apply.
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, or cherry-picked onto current code on a review branch. Trying one out, checking whether it still applies, getting a checkout back onto a clean current branch. Rebasing your own commit 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 actually in.
Keep this skill as routing and stopping rules; the refs, the remotes and the
commands are lookups, and a copy of them here goes stale in somebody else's
checkout with nothing to report it.
The reason this is a workflow of its own is what it must not do. A patch that no
longer applies is a finding — the branch moved under a change nobody rebased —
and a session that quietly resolves its way to something that compiles has
destroyed that finding and produced a patch nobody wrote. So every step below
has an end, and reaching one is a result.
## Establish the change before touching 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, or with the
issue number where the change was reached through its issue. Four things it
answers decide everything below: the **branch the change targets**, which is
what it has to be applied onto and is regularly not the one you are standing
on; the **patch set that is current on the server**, because an older one is
still fetchable and applying it silently reviews a revision nobody is looking
at; the **status**, since MERGED and ABANDONED are both answers that end the
work; and the **commit**, which is what says afterwards whether the checkout
holds the revision under review.
3. `typo3_rule_lookup` for the Gerrit workflow. It carries the ref a patch set
is fetched by, the one thing about fetching a core change that is not
guessable — which remote the ref is on, which is not the one the checkout
fetches from — and the form the third way in below takes. Those are sections
of one page and a search returns the one your words matched, so where the
fetch is the task read it whole: `typo3_rule_lookup` with
`documentId="core/contribution/gerrit-workflow"`, which also stands as
`typo3://guides/core/contribution/gerrit-workflow`.
## Three ways in
The patch goes onto the branch it targets in the checkout you are standing in,
into a git worktree beside it, or onto current code as a commit of your own. The
request usually says which.
The first two hold the patch as its author wrote it, and what decides between
them is whether the checkout is free: the branch path needs it to itself, and 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 are gitignored and so are not something git brings, and no
suite runs in it until they are installed there. `typo3_test_run_guide` states
that precondition above its suites, and beside it the one check whose file list
comes from git, which reports success having read nothing in a worktree. Read
both before running anything in one.
The third answers a different question — whether the change still applies to
current code and still passes there — and it is what "cherry-pick it onto main"
asks for. It takes the patch off the code it was written on, so what the
checkout then holds is a commit this session made and not the revision under
review. That commit gets a name, `review/<change number>`, because the rest of
the work reads it: a named branch can be told apart from the checkout's own
work, found again after anything moves, and removed on purpose. Where the
checkout is not free, this way in takes the worktree as the second does, and the
branch is created there.
## Before the checkout is changed
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 is going into, which on
the worktree path is the worktree.
- **The working tree is clean.** Uncommitted work and a fetched patch set on top
of each other cannot be told apart afterwards, and what carrying it does to
the mixture is not recoverable from the checkout alone. Stop and say what is
uncommitted; do not stash it as a convenience. Where that uncommitted work is
what stands in the way, the worktree is the way past it.
- **The target branch is there and current.** A change targeting a release
branch carried onto the wrong one produces conflicts that are an artefact of
the mistake, and they look exactly like a stale patch.
- **Where you are is where you will be able to get back to.** Write down the
commit the checkout is on before anything moves it. A worktree moves nothing,
so what is written down there is where the worktree is: removing 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 and belongs on no local branch of yours for as long as
it is still that commit, which is what the detached checkout and the worktree
say. The fetch is the same on all three paths and only its destination differs:
the checkout detached onto the commit, a worktree created on it while the branch
you were on stays where it is, or the branch named for the change with the
commit carried onto it.
Then establish what you are holding before judging anything about it: the
checkout's commit is the change's current revision, or it is not, and only the
second needs explaining. Carrying it onto current code is the second by
construction, and the section below is what has to be said 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 — running the suites, reading it against code that has since changed, or
because carrying it is what was asked for. Not as a matter of course: a patch
read against the code it was written on is the patch its author wrote, and
moving it 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, because a core patch is exactly one commit. Either way what
comes out is a commit made here that exists nowhere else, which is why it is 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. That is itself an answer about the change: it
still applies.
Say which commit every result after this is about. The carried commit's hash is
not the patch set's, and a finding quoting the local one without saying so is
about a revision nobody else can look up.
Where it conflicts, [references/checklist.md](references/checklist.md) is what
decides whether to resolve or to stop, one conflict at a time. Read it at the
first conflict rather than after resolving a few — the rule it carries is about
what you are allowed to know, and it cannot be applied backwards.
## Stopping is the normal ending
When a rule above ends the work, undo what was started rather than leaving the
checkout half-way — the section below is that undo, and it is the same one that
ends a run which went fine. A checkout left half-way through a carry is a trap
for whoever opens it next, including you.
Report what was found and not what was attempted. The change, its patch set, its
target branch, how far it got, and the specific thing that stopped it — the
files that conflicted, the hunks, and why the change alone did not decide them.
That report is the useful outcome, and it is what somebody rebasing the patch
properly starts from.
## 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, and a check that inspected no files is not a green. The second is
what a worktree does by default, and what has to be installed there before any
suite runs at all is in the same answer.
Say which branch and which patch set every result is about, which working copy
it ran in where that was a worktree, and which commit where the patch was
carried. A green reported without them is unattributable the moment a new patch
set is pushed.
## Put the checkout back
A checkout sitting on somebody's patch set is not a state to leave behind, and
it is not a state to start the next piece of work from either. Restoring it is a
step of its own, taken 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.
1. **End whatever is in progress first.** A carry that is half applied, or one
that stopped in a conflict, owns the working tree until it is aborted, and
every later step fails against it in a way that reads like something else.
2. **Return to the branch that was recorded at the start**, not to whichever
branch looks right. Where the patch was only fetched, it was on no branch, so
leaving it loses nothing that is not still on the review server — but say the
commit in the answer, because that is the only local name it had.
3. **Delete the branch the patch was carried onto**, where there was one. That
commit is on no review server and in no other branch, so the branch is the
only thing holding it and git refuses the ordinary deletion: the forced one
is the step, and the refusal is the last moment anything asks whether it is
really disposable. Read what is about to go rather than reaching past it —
what was resolved in a conflict goes with it. Then say in the answer that the
branch is gone, because "the checkout is back on its branch" is true with the
review branch still sitting beside it.
4. **Establish that nothing of the patch is left.** An aborted carry can leave
files the change added lying untracked, and 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 is
untracked are two different questions and both have to be asked.
5. **Update the branch from the remote it is fetched 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 — and the change refs live on
only one of them. Take the update as a fast-forward: a merge commit on a
local branch tracking the core is a state nothing here asked for.
6. **Bring the installed dependencies back in step with the branch.** Moving
between a patch set and current code can change what the lock file pins, and
a suite run against dependencies belonging to the other revision fails for a
reason that is not in the diff. This is the step that is skipped and then
spends an hour being diagnosed 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 is left is ending whatever is in progress
and then removing the worktree, which is refused while anything in it is
modified or untracked: that refusal is step 4 arriving one layer out, and
forcing past it throws away the only copy of whatever was resolved in there.
Step 3 stands there too, because removing a worktree leaves the branch it was
created on behind.
Say the end state in the answer: which branch, which commit, and that the tree
is clean; on the worktree path, that the worktree is gone, or where it still is
and why keeping it was deliberate; and where the patch was carried, that the
branch it was carried on is deleted. "Restored" without those is the claim
rather than the result.
This skill owns getting a change under review into a checkout and back out of
it: finding it, fetching the patch set, putting it on the branch it targets,
into a worktree beside it, or onto current code on a branch named for the
change, resolving what the change itself decides, stopping where it does not,
and leaving 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 is
what runs whichever way the rest went. It does not own judging the patch — where
the request is to say what is wrong with it, `typo3-core-patch-review` owns
that, and it starts from the working copy this leaves the patch in, worktree,
checkout or review branch, before the undo runs; carry across which commit the
findings will be about. It does not own changing the patch either: amending a
change into a new patch set and pushing it belongs to
`typo3-core-patch-development`, and carry over the change number, the patch set
that was fetched and whether it had to be carried onto current code to apply.
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 connected or not, and neither side notices. So the first
call below is also the check.
- No `typo3_` tool in this session, or a first call that errors: stop, say this
workflow needs the server and it is not there, and name what came back.
- Do not fall back to general TYPO3 knowledge or start reading the checkout.
That answer carries this workflow's order and confidence and none of its
evidence, and nothing in it says which of the two it is.
- Continue only when asked to after saying so, and repeat it in the answer and
in every finding a lookup would have carried.
## The order
This is the order, and it is an order rather than a list because each step
decides what the next one is worth. A convention fetched after the code has been
read confirms a view instead of testing it. A command recommended before the
project's own are known is a guess that sounds like advice. Where a step below
carries a condition for skipping it, that condition is narrow on purpose: a
prescription that gets skipped teaches the next reader to skip the ones that
matter too.
1. **`typo3_project_describe`** — the installation, its TYPO3 and PHP version,
the extensions that are the project's own, its sites, and the commands this
repository actually declares. Every later answer is filtered by that version,
and a check the repository does not declare is a wrong answer however
sensible it sounds. It ends with the whole procedures this server carries, as
ids: that list is the only place they are named to a client that renders no
resource list, and each one is a `typo3_rule_lookup` with that `documentId`
rather than a search.
2. **`typo3_extension_describe`** for each extension in scope — what it
registers, and what it ships beside that: its manual, its README, its test
layers, its XLF files with the source language each one declares. What it
does *not* ship is answered too, and that is the half no file listing can
give you.
Where step 1 reported none — a core checkout is the case, since it names the
project's own extensions and not TYPO3's — that answer is this step, and
there is nothing to call. Say so. A step passed over in silence cannot be
told from one that was dropped.
3. **`typo3_task_guide`** with a short English task, the paths it touches, the
target version and the change type — 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 brief is built
from the paths as well as the task text, and no skill knows which paths the
caller is holding: a skill that covers the task is not that brief, and
skipping the step costs the hints and the core checks those paths match.
Where the guide's own answer is what named this skill, this is one call for
an answer already in the session. That is 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 this step is still
owed. A brief that carried everything the lookup matched says so — "these are
everything typo3_hint_lookup matches for these paths" — and there the call is
made and asking again returns the same hints. One that stopped short says
that instead and names the ids it left, and those are what is owed: fetch
them by id rather than repeating the query. Read the sentence rather than the
populated `hints` key, which is present either way and does not tell the two
apart.
5. **`typo3_changelog_lookup` with `type: deprecation`**, at each major the
package declares, bounded by `tag` and with the query omitted. Those three
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, and the core titled those entries about its own code. Words taken
from what step 2 reported are therefore matched against titles they were
never written in, and the sweep comes back empty however right the query
looks.
Step 2 picks the tags instead. `ext:core`, `ext:frontend`, `ext:form` and the
rest name the system extension a change is **in** — one call for each one the
package requires, renders through or registers into, which is more than its
manifest lists — and `TCA`, `TypoScript`, `Fluid`, `YAML`, `Backend`,
`Frontend` name the surface, one for each kind of file it ships. An extension
key of your own is not among them and matches nothing. Every call also
returns every tag that version and type carry, so the second call onwards is
read off the first rather than guessed at.
Step 2 is what the answers are checked against, which is the other half the
words were doing. Verify each identifier that comes back in the checkout — a
deprecation nothing here calls is not a finding — and carry the
`FullyScanned` / `PartiallyScanned` tag into the answer, because it says
whether the Extension Scanner can find the remaining call sites or whether
that reading is yours. Bounded this way the sweep is still writable before a
file is opened, which is why it is a step of the order: one left to the
reading reaches only what a finding stumbled into, and the deprecation that
decides whether the package survives the next major is not usually the one a
finding walks past.
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 — 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 and section paths, never
the text of a page, so a PHP identifier has no page to be titled after. It
reaches whatever its own words happen to spell instead. An identifier goes to
`typo3_changelog_lookup` under its own name, which reaches the entries
writing it however the change was titled, and then 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 task that produces no change does not reach this step at all. The property
is what the task produces, and a triage, a reproduction and a review are
illustrations of it rather than the list it is read off. The sweep asks what
a package will have to stop calling, and a task that writes nothing is not
going to call anything. Followed literally in a triage of one issue it costs
one call per declared major per tag and none of them bears on the report.
The exemption ends where the workflow produces a change. A review asked to
make the change is that other workflow, and it starts this order again
holding the files it is about to write.
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 and it is empty before it is run. That condition is worth stating
because this is the most expensive step of the order: one call per declared
major per tag. Which side a change falls on is read off the files it touches
and never off the task it started as — one PHP file edited along the way puts
it back among the ordinary ones, and a skip there costs the deprecation no
finding would have walked into.
A report names the step it did not reach, under either exemption. A step
passed over in silence cannot be told from one that was dropped, which is
what step 2 already asks of itself.
**Then** read the checkout. Not before: listing the files first makes everything
after the listing look optional, and the conventions arrive as a footnote to a
verdict that has already formed.
## When the lookups run out
A behaviour question that survives the lookups above is read out of the
installed source rather than guessed at. What answers it is the class that
implements the behaviour and the one it inherits from. That reading is the step
after the lookups, and what it replaces is changing the code until it works.
What it settles is what this installation does and never what TYPO3 supports. So
a finding says the question could not be settled beyond the version installed,
and an answer built on the reading names the version it holds for.
## Two kinds of lookup, and neither stands in for the other
`typo3_backend_module_lookup`, `typo3_icon_lookup`, `typo3_label_lookup`,
`typo3_fluid_namespace_list` and `typo3_configuration_lookup` report what is
registered, what a path resolves to, what a value really is at runtime. They
establish the facts of this installation and they are never a verdict on it.
`typo3_hint_lookup` and `typo3_documentation_lookup` say whether those facts are
right. A subsystem confirmed by its own runtime lookup can still break every
rule that governs it, so it is not established until both were asked.
## A rule is read in both directions
It says what new code should do, and it says what this checkout is already doing
wrong. A file that has 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 for costing it. Before
reporting one, find what it is there for — the manual, the README, the
changelog, the setting it is driven by, the versions the package declares it
supports — and say so. Where a purpose is documented, what you have is a
trade-off to name with its cost and its alternative, not a defect; where you
cannot find one, the finding says that it could not be established rather than
that none exists. This is the other direction of the rule above, and skipping it
turns a review into 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 that was read, at its path and its line; a
command that was run, with what it printed; a mechanism traced into an installed
package. Say which of the three it is. Leaving it unsaid gives a finding read
out of a CI file the weight of one with a verified line, and the reader has no
way to separate them again.
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, and the linter the
repository already declares is the cheapest evidence in it. A change is not run
under that instruction, and an unknown — a test suite, a shell pipeline, a
console command — is named in the answer as evidence that is available rather
than run unasked. What a check prints is not the finding: the configuration that
makes it fail is still what the finding is about, and the run is what takes that
finding from derived to established.
## What this server does not know
It does not read your working tree. Which files changed, which branch you are
on, and whether a path or an identifier still exists there are yours to
establish — then pass the concrete paths back, because that is what turns a
general convention into an answer about this code.
## Query it in English
The knowledge is written in English and matched lexically, so a query in another
language reaches the loanwords the two happen to share and nothing else.
Translate the subject before calling and the answer back afterwards, whatever
language you are speaking 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 connected or not, and neither side notices. So the first
call below is also the check.
- No `typo3_` tool in this session, or a first call that errors: stop, say this
workflow needs the server and it is not there, and name what came back.
- Do not fall back to general TYPO3 knowledge or start reading the checkout.
That answer carries this workflow's order and confidence and none of its
evidence, and nothing in it says which of the two it is.
- Continue only when asked to after saying so, and repeat it in the answer and
in every finding a lookup would have carried.
## The order
This is the order, and it is an order rather than a list because each step
decides what the next one is worth. A convention fetched after the code has been
read confirms a view instead of testing it. A command recommended before the
project's own are known is a guess that sounds like advice. Where a step below
carries a condition for skipping it, that condition is narrow on purpose: a
prescription that gets skipped teaches the next reader to skip the ones that
matter too.
1. **`typo3_project_describe`** — the installation, its TYPO3 and PHP version,
the extensions that are the project's own, its sites, and the commands this
repository actually declares. Every later answer is filtered by that version,
and a check the repository does not declare is a wrong answer however
sensible it sounds. It ends with the whole procedures this server carries, as
ids: that list is the only place they are named to a client that renders no
resource list, and each one is a `typo3_rule_lookup` with that `documentId`
rather than a search.
2. **`typo3_extension_describe`** for each extension in scope — what it
registers, and what it ships beside that: its manual, its README, its test
layers, its XLF files with the source language each one declares. What it
does *not* ship is answered too, and that is the half no file listing can
give you.
Where step 1 reported none — a core checkout is the case, since it names the
project's own extensions and not TYPO3's — that answer is this step, and
there is nothing to call. Say so. A step passed over in silence cannot be
told from one that was dropped.
3. **`typo3_task_guide`** with a short English task, the paths it touches, the
target version and the change type — 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 brief is built
from the paths as well as the task text, and no skill knows which paths the
caller is holding: a skill that covers the task is not that brief, and
skipping the step costs the hints and the core checks those paths match.
Where the guide's own answer is what named this skill, this is one call for
an answer already in the session. That is 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 this step is still
owed. A brief that carried everything the lookup matched says so — "these are
everything typo3_hint_lookup matches for these paths" — and there the call is
made and asking again returns the same hints. One that stopped short says
that instead and names the ids it left, and those are what is owed: fetch
them by id rather than repeating the query. Read the sentence rather than the
populated `hints` key, which is present either way and does not tell the two
apart.
5. **`typo3_changelog_lookup` with `type: deprecation`**, at each major the
package declares, bounded by `tag` and with the query omitted. Those three
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, and the core titled those entries about its own code. Words taken
from what step 2 reported are therefore matched against titles they were
never written in, and the sweep comes back empty however right the query
looks.
Step 2 picks the tags instead. `ext:core`, `ext:frontend`, `ext:form` and the
rest name the system extension a change is **in** — one call for each one the
package requires, renders through or registers into, which is more than its
manifest lists — and `TCA`, `TypoScript`, `Fluid`, `YAML`, `Backend`,
`Frontend` name the surface, one for each kind of file it ships. An extension
key of your own is not among them and matches nothing. Every call also
returns every tag that version and type carry, so the second call onwards is
read off the first rather than guessed at.
Step 2 is what the answers are checked against, which is the other half the
words were doing. Verify each identifier that comes back in the checkout — a
deprecation nothing here calls is not a finding — and carry the
`FullyScanned` / `PartiallyScanned` tag into the answer, because it says
whether the Extension Scanner can find the remaining call sites or whether
that reading is yours. Bounded this way the sweep is still writable before a
file is opened, which is why it is a step of the order: one left to the
reading reaches only what a finding stumbled into, and the deprecation that
decides whether the package survives the next major is not usually the one a
finding walks past.
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 — 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 and section paths, never
the text of a page, so a PHP identifier has no page to be titled after. It
reaches whatever its own words happen to spell instead. An identifier goes to
`typo3_changelog_lookup` under its own name, which reaches the entries
writing it however the change was titled, and then 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 task that produces no change does not reach this step at all. The property
is what the task produces, and a triage, a reproduction and a review are
illustrations of it rather than the list it is read off. The sweep asks what
a package will have to stop calling, and a task that writes nothing is not
going to call anything. Followed literally in a triage of one issue it costs
one call per declared major per tag and none of them bears on the report.
The exemption ends where the workflow produces a change. A review asked to
make the change is that other workflow, and it starts this order again
holding the files it is about to write.
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 and it is empty before it is run. That condition is worth stating
because this is the most expensive step of the order: one call per declared
major per tag. Which side a change falls on is read off the files it touches
and never off the task it started as — one PHP file edited along the way puts
it back among the ordinary ones, and a skip there costs the deprecation no
finding would have walked into.
A report names the step it did not reach, under either exemption. A step
passed over in silence cannot be told from one that was dropped, which is
what step 2 already asks of itself.
**Then** read the checkout. Not before: listing the files first makes everything
after the listing look optional, and the conventions arrive as a footnote to a
verdict that has already formed.
## When the lookups run out
A behaviour question that survives the lookups above is read out of the
installed source rather than guessed at. What answers it is the class that
implements the behaviour and the one it inherits from. That reading is the step
after the lookups, and what it replaces is changing the code until it works.
What it settles is what this installation does and never what TYPO3 supports. So
a finding says the question could not be settled beyond the version installed,
and an answer built on the reading names the version it holds for.
## Two kinds of lookup, and neither stands in for the other
`typo3_backend_module_lookup`, `typo3_icon_lookup`, `typo3_label_lookup`,
`typo3_fluid_namespace_list` and `typo3_configuration_lookup` report what is
registered, what a path resolves to, what a value really is at runtime. They
establish the facts of this installation and they are never a verdict on it.
`typo3_hint_lookup` and `typo3_documentation_lookup` say whether those facts are
right. A subsystem confirmed by its own runtime lookup can still break every
rule that governs it, so it is not established until both were asked.
## A rule is read in both directions
It says what new code should do, and it says what this checkout is already doing
wrong. A file that has 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 for costing it. Before
reporting one, find what it is there for — the manual, the README, the
changelog, the setting it is driven by, the versions the package declares it
supports — and say so. Where a purpose is documented, what you have is a
trade-off to name with its cost and its alternative, not a defect; where you
cannot find one, the finding says that it could not be established rather than
that none exists. This is the other direction of the rule above, and skipping it
turns a review into 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 that was read, at its path and its line; a
command that was run, with what it printed; a mechanism traced into an installed
package. Say which of the three it is. Leaving it unsaid gives a finding read
out of a CI file the weight of one with a verified line, and the reader has no
way to separate them again.
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, and the linter the
repository already declares is the cheapest evidence in it. A change is not run
under that instruction, and an unknown — a test suite, a shell pipeline, a
console command — is named in the answer as evidence that is available rather
than run unasked. What a check prints is not the finding: the configuration that
makes it fail is still what the finding is about, and the run is what takes that
finding from derived to established.
## What this server does not know
It does not read your working tree. Which files changed, which branch you are
on, and whether a path or an identifier still exists there are yours to
establish — then pass the concrete paths back, because that is what turns a
general convention into an answer about this code.
## Query it in English
The knowledge is written in English and matched lexically, so a query in another
language reaches the loanwords the two happen to share and nothing else.
Translate the subject before calling and the answer back afterwards, whatever
language you are speaking with the user.
Resolve or stop#
# Resolve or stop
One rule decides every conflict, and it is about what you are allowed to know:
**A conflict is resolvable only where the change itself decides it.** The patch
says what it wants, the branch says what is there now, and where those two
together leave exactly one way to write the hunk, writing it is transcription.
Where they leave a choice, the choice is the author's and taking it produces a
patch nobody wrote — which then gets read, run and reported on as if it were
theirs.
Apply it per conflict, not per file. A change can be transcription in one hunk
and somebody else's decision in the next, and the second one ends the work
whatever happened in the first.
## Resolve
- **Context moved.** The lines the patch touches are unchanged; something above
or below them shifted. Nothing to decide.
- **A rename the branch made and the patch predates.** The change's intent is
unchanged under the new name, and the new name is what the branch uses
everywhere. Verify the second before believing the first.
- **Both sides made the same edit.** Keeping one copy is the only reading.
- **Import, use-statement and namespace ordering.** 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.** Rewriting a call site to
the new API is writing the patch, and it is also the moment the change might
no longer be needed 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.
- **Resolving would need the issue to be re-read to decide it.** That is the
signal that the change no longer carries its own answer.
- **More than a handful of hunks conflict.** Individually resolvable or not, the
patch is stale enough that its author rebasing it is the honest outcome; say
how many and where.
## What a stop reports
- The change, its patch set, its target branch, and the commit the checkout was
put 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 is what makes the report
usable by whoever rebases it properly, and it is the part a diff does not say.
- Whether anything was resolved before the stop, and what. A partially resolved
carry that was then aborted still tells the next person which hunks are free.
## After any resolution
- The build and the suites that cover the touched paths are what says 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, and reporting one as the other is
the failure this whole file guards.
- The carried state is local. It is not pushed, and pushing it would be opening
a patch set in somebody else's name — that belongs to the workflow that owns
amending a change, and only where the change is yours to amend.
markdown
# Resolve or stop
One rule decides every conflict, and it is about what you are allowed to know:
**A conflict is resolvable only where the change itself decides it.** The patch
says what it wants, the branch says what is there now, and where those two
together leave exactly one way to write the hunk, writing it is transcription.
Where they leave a choice, the choice is the author's and taking it produces a
patch nobody wrote — which then gets read, run and reported on as if it were
theirs.
Apply it per conflict, not per file. A change can be transcription in one hunk
and somebody else's decision in the next, and the second one ends the work
whatever happened in the first.
## Resolve
- **Context moved.** The lines the patch touches are unchanged; something above
or below them shifted. Nothing to decide.
- **A rename the branch made and the patch predates.** The change's intent is
unchanged under the new name, and the new name is what the branch uses
everywhere. Verify the second before believing the first.
- **Both sides made the same edit.** Keeping one copy is the only reading.
- **Import, use-statement and namespace ordering.** 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.** Rewriting a call site to
the new API is writing the patch, and it is also the moment the change might
no longer be needed 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.
- **Resolving would need the issue to be re-read to decide it.** That is the
signal that the change no longer carries its own answer.
- **More than a handful of hunks conflict.** Individually resolvable or not, the
patch is stale enough that its author rebasing it is the honest outcome; say
how many and where.
## What a stop reports
- The change, its patch set, its target branch, and the commit the checkout was
put 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 is what makes the report
usable by whoever rebases it properly, and it is the part a diff does not say.
- Whether anything was resolved before the stop, and what. A partially resolved
carry that was then aborted still tells the next person which hunks are free.
## After any resolution
- The build and the suites that cover the touched paths are what says 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, and reporting one as the other is
the failure this whole file guards.
- The carried state is local. It is not pushed, and pushing it would be opening
a patch set in somebody else's name — that belongs to the workflow that owns
amending a change, and only where the change is yours to amend.