TYPO3 Extension Upgrade
Skill: typo3-extension-upgrade
Carry a TYPO3 extension, sitepackage or project package from the TYPO3 and PHP versions it supports today to another set: adding a new major, dropping one that is no longer maintained, replacing what a major deprecated or removed, and proving every version it claims.
Markdown source#
---
name: typo3-extension-upgrade
description: 'Carry a TYPO3 extension, sitepackage or project package from the TYPO3 and PHP versions it supports today to another set: adding a new major, dropping one that is no longer maintained, replacing what a major deprecated or removed, and proving every version it claims.'
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 Extension Upgrade
Cross a package from the range it declares to the range it is meant to declare,
in an order where each step decides what the next one is worth. Keep this skill
as routing and workflow; never retain version-specific APIs, constraints,
replacements, or the contents of a changelog — every one of those is a property
of the installation being read and of the target being aimed at.
## The order
1. Work through [references/base.md](references/base.md). It fixes what this
package is, what it ships, and — as its last step before the checkout — the
sweep of the installed core's deprecations over that surface. This workflow
starts from the result of that sweep rather than restating it.
2. Widen the sweep, below, into the work list.
3. Resolve the range the package may declare, below. Not before: what breaks is
what decides whether a range is reachable at all.
4. Change what the list justifies, and nothing else.
5. Prove it against every combination the package declares.
## Widen the sweep into a work list
The base sweeps one source. An upgrade needs three, because each reaches call
sites the others cannot:
- **The changelog**, as the base sweeps it, and `typo3_changelog_lookup` again
with `type: breaking` — same tags, same majors, still no query. A review asks
what will stop working; an upgrade also asks what already has.
- **The Extension Scanner**, in the installation's own Upgrade module — it needs
a reachable backend and an administrator, and it reads the extension's
installed files. It finds the call sites of what its matchers cover, and the
`FullyScanned` / `PartiallyScanned` tag the base carries out of the changelog
is what says whether its silence on an entry means anything. A clean scan for
a partially scanned entry is not a result; those call sites are yours to find.
- **The deprecation annotations on what this package actually calls**, in the
installed core and in the packages it depends on. A changelog entry is per
release and is reached by the tags of the system extension it sits in; an
annotation sits on the class, method or property itself, so a symbol whose
entry falls outside the tags the sweep named — or carries no `ext:` tag at all
— is reached only this way, and a class deprecated as a whole takes every call
site of it with it.
Both the changelog and the scanner answer from the **core that is installed**,
which is the boundary this whole order rests on: they say what this package owes
the majors it already runs on, and they do not know what the target major
changed until the installation is on it. Until then the target's changes come
from official documentation for that version, never from memory — a list of
"what the new major changed" written from recall reads exactly like one that was
looked up. Once the resolution below puts the installation on the target, run
the sweep again there; that second pass is what says the work is done.
Write the result down before changing a file: one entry per call site, with the
identifier, the path and line in this package, which declared major deprecates
or removes it, and which of the three established it. That list is the work, and
it is what the result closes on — including the entries that came back empty,
with the majors they covered.
## Resolve the range, rather than assert it
- The declared range is in the Composer manifest and in `ext_emconf.php`, and
the two either say the same thing or the difference is itself a finding: for a
non-Composer installation `ext_emconf.php` is the only constraint that
governs.
- The PHP range is the intersection of what every declared TYPO3 major supports,
never the PHP the current machine happens to run.
- Where the package requires a system extension, establish that the target still
ships it: `typo3_system_extension_lookup` answers by key and package name and
does not need it installed. One that stopped being part of the core is a
requirement that cannot resolve, and the replacement is a decision, not a
rename.
- Let the dependency solver answer, and quote what it printed. A constraint that
should work and one the solver accepts are different claims, and the solver
reports a third-party dependency without a release for the target as a
conflict rather than as advice — that dependency then decides the schedule.
- Dropping a major is the user's decision, never one taken to make the code
simpler. Widening to a new one keeps every version currently declared unless
the request says otherwise.
## The boundary of what may change
The **lowest declared major decides every shape in the package**. A registration
form, attribute or API introduced later cannot replace one that still has to
work there, so a runtime branch on the major and a registration written the
older way are what the declared range requires — not debt to clean up. Say so
where the code already does it; the alternative is an upgrade that breaks the
version it was told to keep.
Where a replacement belongs to a subsystem the base did not have in scope, ask
its conventions before writing it: `typo3_hint_lookup` with the concrete paths,
and `typo3_documentation_lookup` with the target version where the official API
decides the shape. Where nothing in the declared range replaces a removal, that
is the answer — one package version cannot serve both, and choosing silently is
how a supported version stops working without anyone noticing.
Change what the work list justifies. An upgrade is not a modernization, a
cleanup or a rewrite: anything the list did not produce goes to the workflow
that owns it — `typo3-extension-conformance` for what else is wrong with the
package, `typo3-extension-testing` for coverage the upgrade wants but does not
have, `typo3-extension-documentation` for the manual that now describes a
different range.
## Prove it on every version it claims
1. Build the matrix from the declaration, not from convenience: every TYPO3
major the package declares against the PHP versions that major supports.
2. Resolve each cell before running it, and treat a cell that will not resolve
as a result — it is the finding, and skipping it is what lets a package
declare a version nobody has ever installed it on.
3. Run the repository's own commands per cell, the checks first. A step that
runs in only one cell leaves the others unproven, and that includes the ones
the repository's CI declares.
4. Report the work list with every entry closed or explicitly left open, the
resolutions with what the solver printed, what changed and what deliberately
did not, and the matrix cell by cell. A cell nobody ran is named as unrun
rather than left out — the matrix is the claim the package makes about
itself, and an unrun cell is the part of that claim nothing stands behind.
5. Draft the message with `typo3_commit_message_guide` and `workflow="project"`.
The crossing lands in the package's own repository, and the range it now
declares is what the message is about.
This skill owns crossing a package from one supported range to another: the
sweep that says what breaks, the constraints that say what may be declared, the
changes those two justify, and the proof per declared combination. It does not
own deciding whether the package is otherwise sound, establishing a harness that
is missing, or rewriting the documentation — each of those is named above with
the workflow it belongs to, and the crossing to it is explicit: state the
verified point the upgrade reached, stop before editing that owner's files, and
carry across the range and the call sites already established.
markdown
---
name: typo3-extension-upgrade
description: 'Carry a TYPO3 extension, sitepackage or project package from the TYPO3 and PHP versions it supports today to another set: adding a new major, dropping one that is no longer maintained, replacing what a major deprecated or removed, and proving every version it claims.'
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 Extension Upgrade
Cross a package from the range it declares to the range it is meant to declare,
in an order where each step decides what the next one is worth. Keep this skill
as routing and workflow; never retain version-specific APIs, constraints,
replacements, or the contents of a changelog — every one of those is a property
of the installation being read and of the target being aimed at.
## The order
1. Work through [references/base.md](references/base.md). It fixes what this
package is, what it ships, and — as its last step before the checkout — the
sweep of the installed core's deprecations over that surface. This workflow
starts from the result of that sweep rather than restating it.
2. Widen the sweep, below, into the work list.
3. Resolve the range the package may declare, below. Not before: what breaks is
what decides whether a range is reachable at all.
4. Change what the list justifies, and nothing else.
5. Prove it against every combination the package declares.
## Widen the sweep into a work list
The base sweeps one source. An upgrade needs three, because each reaches call
sites the others cannot:
- **The changelog**, as the base sweeps it, and `typo3_changelog_lookup` again
with `type: breaking` — same tags, same majors, still no query. A review asks
what will stop working; an upgrade also asks what already has.
- **The Extension Scanner**, in the installation's own Upgrade module — it needs
a reachable backend and an administrator, and it reads the extension's
installed files. It finds the call sites of what its matchers cover, and the
`FullyScanned` / `PartiallyScanned` tag the base carries out of the changelog
is what says whether its silence on an entry means anything. A clean scan for
a partially scanned entry is not a result; those call sites are yours to find.
- **The deprecation annotations on what this package actually calls**, in the
installed core and in the packages it depends on. A changelog entry is per
release and is reached by the tags of the system extension it sits in; an
annotation sits on the class, method or property itself, so a symbol whose
entry falls outside the tags the sweep named — or carries no `ext:` tag at all
— is reached only this way, and a class deprecated as a whole takes every call
site of it with it.
Both the changelog and the scanner answer from the **core that is installed**,
which is the boundary this whole order rests on: they say what this package owes
the majors it already runs on, and they do not know what the target major
changed until the installation is on it. Until then the target's changes come
from official documentation for that version, never from memory — a list of
"what the new major changed" written from recall reads exactly like one that was
looked up. Once the resolution below puts the installation on the target, run
the sweep again there; that second pass is what says the work is done.
Write the result down before changing a file: one entry per call site, with the
identifier, the path and line in this package, which declared major deprecates
or removes it, and which of the three established it. That list is the work, and
it is what the result closes on — including the entries that came back empty,
with the majors they covered.
## Resolve the range, rather than assert it
- The declared range is in the Composer manifest and in `ext_emconf.php`, and
the two either say the same thing or the difference is itself a finding: for a
non-Composer installation `ext_emconf.php` is the only constraint that
governs.
- The PHP range is the intersection of what every declared TYPO3 major supports,
never the PHP the current machine happens to run.
- Where the package requires a system extension, establish that the target still
ships it: `typo3_system_extension_lookup` answers by key and package name and
does not need it installed. One that stopped being part of the core is a
requirement that cannot resolve, and the replacement is a decision, not a
rename.
- Let the dependency solver answer, and quote what it printed. A constraint that
should work and one the solver accepts are different claims, and the solver
reports a third-party dependency without a release for the target as a
conflict rather than as advice — that dependency then decides the schedule.
- Dropping a major is the user's decision, never one taken to make the code
simpler. Widening to a new one keeps every version currently declared unless
the request says otherwise.
## The boundary of what may change
The **lowest declared major decides every shape in the package**. A registration
form, attribute or API introduced later cannot replace one that still has to
work there, so a runtime branch on the major and a registration written the
older way are what the declared range requires — not debt to clean up. Say so
where the code already does it; the alternative is an upgrade that breaks the
version it was told to keep.
Where a replacement belongs to a subsystem the base did not have in scope, ask
its conventions before writing it: `typo3_hint_lookup` with the concrete paths,
and `typo3_documentation_lookup` with the target version where the official API
decides the shape. Where nothing in the declared range replaces a removal, that
is the answer — one package version cannot serve both, and choosing silently is
how a supported version stops working without anyone noticing.
Change what the work list justifies. An upgrade is not a modernization, a
cleanup or a rewrite: anything the list did not produce goes to the workflow
that owns it — `typo3-extension-conformance` for what else is wrong with the
package, `typo3-extension-testing` for coverage the upgrade wants but does not
have, `typo3-extension-documentation` for the manual that now describes a
different range.
## Prove it on every version it claims
1. Build the matrix from the declaration, not from convenience: every TYPO3
major the package declares against the PHP versions that major supports.
2. Resolve each cell before running it, and treat a cell that will not resolve
as a result — it is the finding, and skipping it is what lets a package
declare a version nobody has ever installed it on.
3. Run the repository's own commands per cell, the checks first. A step that
runs in only one cell leaves the others unproven, and that includes the ones
the repository's CI declares.
4. Report the work list with every entry closed or explicitly left open, the
resolutions with what the solver printed, what changed and what deliberately
did not, and the matrix cell by cell. A cell nobody ran is named as unrun
rather than left out — the matrix is the claim the package makes about
itself, and an unrun cell is the part of that claim nothing stands behind.
5. Draft the message with `typo3_commit_message_guide` and `workflow="project"`.
The crossing lands in the package's own repository, and the range it now
declares is what the message is about.
This skill owns crossing a package from one supported range to another: the
sweep that says what breaks, the constraints that say what may be declared, the
changes those two justify, and the proof per declared combination. It does not
own deciding whether the package is otherwise sound, establishing a harness that
is missing, or rewriting the documentation — each of those is named above with
the workflow it belongs to, and the crossing to it is explicit: state the
verified point the upgrade reached, stop before editing that owner's files, and
carry across the range and the call sites already established.
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.