TYPO3 Extension Asset Build
Skill: typo3-extension-asset-build
The asset build of a TYPO3 extension, sitepackage or project package: npm and package.json dependency updates, Dependabot pull requests, webpack, vite, Grunt or Sass, the built CSS and JavaScript under Resources/Public, the import map it reaches the backend by, and the core classes and icons it borrows. Stops at a bundler or library migration.
Markdown source#
---
name: typo3-extension-asset-build
description: 'The asset build of a TYPO3 extension, sitepackage or project package: npm and package.json dependency updates, Dependabot pull requests, webpack, vite, Grunt or Sass, the built CSS and JavaScript under Resources/Public, the import map it reaches the backend by, and the core classes and icons it borrows. Stops at a bundler or library migration.'
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 Asset Build
A package's own build produces the CSS and JavaScript its backend and frontend
load. Most of that task is npm's, the bundler's and the library's. What this
workflow orders is the TYPO3 half. That is what you run the build as, what its
output promises the backend, and which output the repository commits. Keep this
skill as routing and workflow.
Never keep a dependency version, a bundler configuration, a build command or a
core class name. Every one of those is a property of the repository in front of
you and of the majors it declares.
## The order
1. Work through [references/base.md](references/base.md). It fixes what this
package is, which majors it declares, and what it runs its build with.
2. Establish which of the output the repository commits, and which route each
file reaches a page by, below.
3. Verify every core surface the change will borrow, before you write it, below.
4. Change what the task asks for. Stop where the library's own migration begins.
5. Rebuild, and check what the output promises: the backend below, then the
frontend below.
6. Commit the rebuilt artefacts together with the source that produced them.
The first step discharges `typo3_project_describe`. It reports the manifests
this repository keeps: at the root, and one directory down where the build sits
there.
It reports the commands each of them declares with the manifest they came
from. It says whether a command reports or changes. It reports the Node that
the manifest, the pinned version, the CI workflow and the container each
state. It names the disagreements between them.
Run the commands as it reported them. An invocation you rewrite from habit
runs the build in the wrong directory or on the wrong Node. Both of those
surface as a diff nobody can explain.
**Read a pin against the release current on the day.** That step reports what
each source states, and none of them says whether it is still current. So
establish the current release where its publisher announces it. That is the
runtime's own release schedule, the package registry, the repository that tags
an action. Report every pin behind it as a finding that carries the raise.
What speaks against one is a bound this repository declares: the Node the build
needs, the majors the package supports. The finding then names the newest
release that bound allows. The raise is the maintainer's, and to move the bound
is a task of its own.
## Which of the output is committed, and how it reaches a page
This server does not read your working tree. So these are the repository's
answers and not a lookup. Does git track the built output? Does any check assert
that a build leaves the tree clean? Over which route does each output file reach
a page?
- **Committed output means source and output change together.** The package's
consumers install what is in the repository. So a commit with new source and
last month's artefacts ships the old behaviour to every one of them.
- **Uncommitted output means the deployment runs the build.** Then the artefacts
are not yours to commit. What has to hold instead is that the build runs where
the deployment runs it.
- A check that asserts a clean tree after a build is the executable form of that
decision. Where the repository has none, its absence is a finding rather than
a licence. `typo3-extension-testing` establishes that check. Invoke
`typo3-extension-testing` for it, and carry the build command and the output
paths you established here.
- **Each output file reaches a page through a declaration somewhere, and the
route differs between the two halves.** An import map declares backend
JavaScript. TypoScript, the AssetCollector behind `<f:asset.css>`, or
`PageRenderer` includes a frontend stylesheet.
Find which route this package uses for each file before you change anything.
Step 5 checks that the same route still carries afterwards, and it cannot find
a route it never saw.
## When the build is not reproducible
A build whose output differs on every run makes every later diff unreadable.
After a change the two causes look the same. Output differs because of your
change, or it differs whatever the source says.
- **The repository's own check answers this.** A job that asserts a clean tree
after a build is the executable form of that question. Step 2 has already
established whether there is one. Run it.
- Where the rebuild in step 5 produces a diff your change does not explain,
build the unchanged checkout and compare. That separates the two causes. It
costs nothing in the ordinary case, where the diff has an explanation.
- Where the tree comes back dirty on an unchanged checkout, that is the finding.
It is about the toolchain rather than about the package. Say which file
differs and how.
- Where the build does not run at all, first compare the reported Node with what
this machine has. Reinstall the dependency tree second.
## Where this workflow stops
The bundler's configuration format, a library's own API change and a defect in
the runtime belong to that project's manual. Read them there, and say in the
answer which manual answered. A migration you reconstruct from the installed
sources of a dependency is a reading of one version of it. It is worth what it
says about that version alone.
Two changes look like this task and are not:
- The package moves to another set of TYPO3 majors, or what one of them removed
broke it. That decides the whole reading below. So invoke
`typo3-extension-upgrade` and carry across the build commands and the output
paths you established.
- The request is an audit of the package rather than a change the user already
agreed to. Invoke `typo3-extension-health` and carry across what you
established about the build.
## Verifying a core surface before you borrow it
Verify a class or an icon the output takes from the core before you write it,
not after. Verified afterwards, it is already in the diff. The cost of a no is
then a second pass over markup that reads as finished.
- `typo3_documentation_lookup`, at each major the package declares, for the
backend JavaScript module contract. It also says what an extension may assume
is already loaded. To assume a library is present because the backend once
shipped it is a decision, and this answer settles it.
- **The query names the component, and the answer places the class.**
`typo3_component_lookup` with the `targetVersion` of a declared major returns
each class with where it sits. That is around the component, on its root
element, or inside it.
`table-fit` is the element *around* a `.table`. Its name does not say so, and
its own stylesheet rule does not either.
- **A class the answer does not place is one the core's stylesheet says nothing
about.** That is not a licence to attach it anywhere. It means the position
has to come from somewhere else. The entry names the core Sass file to read on
that branch.
- **One call per declared major, because the position is itself version-bound.**
A class can stand above its component on one major and not on another. So a
surface verified on the installed major alone has no proof on the rest. The
finding is the range it holds on. A borrowed surface not verified on the
lowest declared major is a defect in that version.
- A class the package's own stylesheet only adds a rule to is one of these. It
reads in the diff exactly like one the package owns.
- `typo3_icon_lookup` for a borrowed icon identifier. It answers from the
installation. So it settles the installed major and says nothing about the
others the package declares.
- `typo3_changelog_lookup`, restricted to each declared major, for a core asset
the output stops relying on. To delete a rule because the core no longer ships
the icon font it names is an unverified decision. So is to attach a class
because the core does. The build goes green either way.
- `typo3_rule_lookup` with `documentId="any/backend/using-the-styleguide"` for
what a styleguide demo states and what it does not. Read it before you take a
demo as the contract for a component.
## What the rebuilt output promises the backend
Built backend JavaScript does not reach the backend because it is present. An
import map declares it, one specifier per file. A build that renames, splits,
hashes or drops an output breaks that map, and nothing fails in PHP. So after
every rebuild, check each mapped path against the file the build wrote.
A pipeline written for the frontend produces the wrong shape here: one hashed
bundle where the map names files. Nothing fails in PHP there either. The
document the frontend section below hands over says which file declares the map
and which check belongs to it.
## What the rebuilt output promises the frontend
You check the frontend half a different way, because nothing about it is a file
to compare. The TypoScript that resolves for a site decides whether a stylesheet
reaches a page. So the check is that the route step 2 found still names the file
the build now writes.
- A rebuild that renames, hashes or moves an output breaks that route as
silently as it breaks the import map. There is no exception either. The
symptom is a page rendered without the styles.
- `typo3_rule_lookup` with `documentId="any/assets/how-an-asset-reaches-a-page"`
for the routes and the check that belongs to each. Step 2 established which
route a file takes. This step checks whether that route still names the file
the build now writes.
- `typo3_hint_lookup` for `Resources/Public/` paths, which reaches
`public-assets`. It says how a package publishes its public files into the
document root, and what makes one resolvable at all. That is version-bound. A
build directory outside the default paths is not the same question on every
major.
- Where an output moved out of `Resources/Public/`, the finding is the publish
step rather than the build. The answer says which of the two it is.
## Closing the change
1. Report what you rebuilt and what the build printed. Report which of the
mapped paths and borrowed surfaces you verified on which majors. Name the
ones that came back withheld or unanswerable.
2. Draft the message with `typo3_commit_message_guide` and `workflow="project"`.
The change lands in the package's own repository.
This skill owns the TYPO3 half of a package's asset build. That is what you run
the build as, and whether the repository commits its output with its source. It
is what the import map promises about the output files. It is which majors a
borrowed core class or icon holds on.
It does not own the migration a bundler or a JavaScript library asks for on its
own account. It does not own the range of TYPO3 majors the package declares. It
does not own the audit that decides what else is wrong with it. It does not own
the harness that would prove the build. The sections above name each of those
with the workflow or the manual it belongs to.
markdown
---
name: typo3-extension-asset-build
description: 'The asset build of a TYPO3 extension, sitepackage or project package: npm and package.json dependency updates, Dependabot pull requests, webpack, vite, Grunt or Sass, the built CSS and JavaScript under Resources/Public, the import map it reaches the backend by, and the core classes and icons it borrows. Stops at a bundler or library migration.'
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 Asset Build
A package's own build produces the CSS and JavaScript its backend and frontend
load. Most of that task is npm's, the bundler's and the library's. What this
workflow orders is the TYPO3 half. That is what you run the build as, what its
output promises the backend, and which output the repository commits. Keep this
skill as routing and workflow.
Never keep a dependency version, a bundler configuration, a build command or a
core class name. Every one of those is a property of the repository in front of
you and of the majors it declares.
## The order
1. Work through [references/base.md](references/base.md). It fixes what this
package is, which majors it declares, and what it runs its build with.
2. Establish which of the output the repository commits, and which route each
file reaches a page by, below.
3. Verify every core surface the change will borrow, before you write it, below.
4. Change what the task asks for. Stop where the library's own migration begins.
5. Rebuild, and check what the output promises: the backend below, then the
frontend below.
6. Commit the rebuilt artefacts together with the source that produced them.
The first step discharges `typo3_project_describe`. It reports the manifests
this repository keeps: at the root, and one directory down where the build sits
there.
It reports the commands each of them declares with the manifest they came
from. It says whether a command reports or changes. It reports the Node that
the manifest, the pinned version, the CI workflow and the container each
state. It names the disagreements between them.
Run the commands as it reported them. An invocation you rewrite from habit
runs the build in the wrong directory or on the wrong Node. Both of those
surface as a diff nobody can explain.
**Read a pin against the release current on the day.** That step reports what
each source states, and none of them says whether it is still current. So
establish the current release where its publisher announces it. That is the
runtime's own release schedule, the package registry, the repository that tags
an action. Report every pin behind it as a finding that carries the raise.
What speaks against one is a bound this repository declares: the Node the build
needs, the majors the package supports. The finding then names the newest
release that bound allows. The raise is the maintainer's, and to move the bound
is a task of its own.
## Which of the output is committed, and how it reaches a page
This server does not read your working tree. So these are the repository's
answers and not a lookup. Does git track the built output? Does any check assert
that a build leaves the tree clean? Over which route does each output file reach
a page?
- **Committed output means source and output change together.** The package's
consumers install what is in the repository. So a commit with new source and
last month's artefacts ships the old behaviour to every one of them.
- **Uncommitted output means the deployment runs the build.** Then the artefacts
are not yours to commit. What has to hold instead is that the build runs where
the deployment runs it.
- A check that asserts a clean tree after a build is the executable form of that
decision. Where the repository has none, its absence is a finding rather than
a licence. `typo3-extension-testing` establishes that check. Invoke
`typo3-extension-testing` for it, and carry the build command and the output
paths you established here.
- **Each output file reaches a page through a declaration somewhere, and the
route differs between the two halves.** An import map declares backend
JavaScript. TypoScript, the AssetCollector behind `<f:asset.css>`, or
`PageRenderer` includes a frontend stylesheet.
Find which route this package uses for each file before you change anything.
Step 5 checks that the same route still carries afterwards, and it cannot find
a route it never saw.
## When the build is not reproducible
A build whose output differs on every run makes every later diff unreadable.
After a change the two causes look the same. Output differs because of your
change, or it differs whatever the source says.
- **The repository's own check answers this.** A job that asserts a clean tree
after a build is the executable form of that question. Step 2 has already
established whether there is one. Run it.
- Where the rebuild in step 5 produces a diff your change does not explain,
build the unchanged checkout and compare. That separates the two causes. It
costs nothing in the ordinary case, where the diff has an explanation.
- Where the tree comes back dirty on an unchanged checkout, that is the finding.
It is about the toolchain rather than about the package. Say which file
differs and how.
- Where the build does not run at all, first compare the reported Node with what
this machine has. Reinstall the dependency tree second.
## Where this workflow stops
The bundler's configuration format, a library's own API change and a defect in
the runtime belong to that project's manual. Read them there, and say in the
answer which manual answered. A migration you reconstruct from the installed
sources of a dependency is a reading of one version of it. It is worth what it
says about that version alone.
Two changes look like this task and are not:
- The package moves to another set of TYPO3 majors, or what one of them removed
broke it. That decides the whole reading below. So invoke
`typo3-extension-upgrade` and carry across the build commands and the output
paths you established.
- The request is an audit of the package rather than a change the user already
agreed to. Invoke `typo3-extension-health` and carry across what you
established about the build.
## Verifying a core surface before you borrow it
Verify a class or an icon the output takes from the core before you write it,
not after. Verified afterwards, it is already in the diff. The cost of a no is
then a second pass over markup that reads as finished.
- `typo3_documentation_lookup`, at each major the package declares, for the
backend JavaScript module contract. It also says what an extension may assume
is already loaded. To assume a library is present because the backend once
shipped it is a decision, and this answer settles it.
- **The query names the component, and the answer places the class.**
`typo3_component_lookup` with the `targetVersion` of a declared major returns
each class with where it sits. That is around the component, on its root
element, or inside it.
`table-fit` is the element *around* a `.table`. Its name does not say so, and
its own stylesheet rule does not either.
- **A class the answer does not place is one the core's stylesheet says nothing
about.** That is not a licence to attach it anywhere. It means the position
has to come from somewhere else. The entry names the core Sass file to read on
that branch.
- **One call per declared major, because the position is itself version-bound.**
A class can stand above its component on one major and not on another. So a
surface verified on the installed major alone has no proof on the rest. The
finding is the range it holds on. A borrowed surface not verified on the
lowest declared major is a defect in that version.
- A class the package's own stylesheet only adds a rule to is one of these. It
reads in the diff exactly like one the package owns.
- `typo3_icon_lookup` for a borrowed icon identifier. It answers from the
installation. So it settles the installed major and says nothing about the
others the package declares.
- `typo3_changelog_lookup`, restricted to each declared major, for a core asset
the output stops relying on. To delete a rule because the core no longer ships
the icon font it names is an unverified decision. So is to attach a class
because the core does. The build goes green either way.
- `typo3_rule_lookup` with `documentId="any/backend/using-the-styleguide"` for
what a styleguide demo states and what it does not. Read it before you take a
demo as the contract for a component.
## What the rebuilt output promises the backend
Built backend JavaScript does not reach the backend because it is present. An
import map declares it, one specifier per file. A build that renames, splits,
hashes or drops an output breaks that map, and nothing fails in PHP. So after
every rebuild, check each mapped path against the file the build wrote.
A pipeline written for the frontend produces the wrong shape here: one hashed
bundle where the map names files. Nothing fails in PHP there either. The
document the frontend section below hands over says which file declares the map
and which check belongs to it.
## What the rebuilt output promises the frontend
You check the frontend half a different way, because nothing about it is a file
to compare. The TypoScript that resolves for a site decides whether a stylesheet
reaches a page. So the check is that the route step 2 found still names the file
the build now writes.
- A rebuild that renames, hashes or moves an output breaks that route as
silently as it breaks the import map. There is no exception either. The
symptom is a page rendered without the styles.
- `typo3_rule_lookup` with `documentId="any/assets/how-an-asset-reaches-a-page"`
for the routes and the check that belongs to each. Step 2 established which
route a file takes. This step checks whether that route still names the file
the build now writes.
- `typo3_hint_lookup` for `Resources/Public/` paths, which reaches
`public-assets`. It says how a package publishes its public files into the
document root, and what makes one resolvable at all. That is version-bound. A
build directory outside the default paths is not the same question on every
major.
- Where an output moved out of `Resources/Public/`, the finding is the publish
step rather than the build. The answer says which of the two it is.
## Closing the change
1. Report what you rebuilt and what the build printed. Report which of the
mapped paths and borrowed surfaces you verified on which majors. Name the
ones that came back withheld or unanswerable.
2. Draft the message with `typo3_commit_message_guide` and `workflow="project"`.
The change lands in the package's own repository.
This skill owns the TYPO3 half of a package's asset build. That is what you run
the build as, and whether the repository commits its output with its source. It
is what the import map promises about the output files. It is which majors a
borrowed core class or icon holds on.
It does not own the migration a bundler or a JavaScript library asks for on its
own account. It does not own the range of TYPO3 majors the package declares. It
does not own the audit that decides what else is wrong with it. It does not own
the harness that would prove the build. The sections above name each of those
with the workflow or the manual it belongs to.
References#
Where every task starts#
# Where every task starts
## Nothing starts until the server answers
A skill is a file the installer left behind. It loads and reads the same whether
the tools behind it are there or not, and neither side notices. So the first
call below is also the check.
- A client may carry this server's name in each tool's name:
`mcp__<server>__typo3_project_describe`. So a search for the bare name comes
back empty where the server is there. A search for a tool's schema needs the
same form. A `select:` on the bare names returns nothing where the tools are
there. Look for the qualified form before you read an empty result as an
answer about the server.
- No `typo3_` tool in this session, or a first call that errors: stop. Say that
this workflow needs the server and it is not there, and name what came back.
- Do not fall back to general TYPO3 knowledge, and do not start to read the
checkout. That answer carries this workflow's order and confidence and none of
its evidence. Nothing in it says which of the two it is.
- Continue only when the user asks you to after you said so. Repeat it in the
answer and in every finding a lookup would have carried.
## The order
This is an order rather than a list. Each step decides what the next one is
worth. Where a step below carries a condition to skip it, that condition is
narrow on purpose. A skipped prescription teaches the next reader to skip the
ones that matter too.
1. **`typo3_project_describe`** — the repository and whether it holds an
installation yet. It reports the TYPO3 and PHP version, the project's own
extensions, its sites, and the commands this repository declares. That
version filters every later answer. A check the repository does not declare
is a wrong answer however sensible it sounds.
The answer ends with the whole procedures this server carries, as ids. That
list is the only place a client that renders no resource list sees their
names. Each one is a `typo3_rule_lookup` with that `documentId` rather than a
search.
2. **`typo3_extension_describe`** for each extension in scope. It says what the
extension registers, and what it ships beside that. That is its manual, its
README, its test layers, and its XLF files with the source language each one
declares. It also says what the extension does *not* ship, and that is the
half no file listing gives you.
Where step 1 reported no extension, that answer is this step, and there is
nothing to call. Say so. A core checkout is that case, because step 1 names
the project's own extensions and not TYPO3's.
3. **`typo3_task_guide`** with a short English task, the paths it touches, the
target version and the change type. It answers the workflow this task belongs
to and the checks that come with it.
Run it in every session, this skill's own tasks included. The guide builds
the brief from the paths as well as the task text. No skill knows which paths
the caller holds.
A skill that covers the task is not that brief. A skipped step costs the
hints and the core checks those paths match. Where the guide's own answer
named this skill, this is one call for an answer already in the session. The
price of a step there is nothing to decide about.
4. **`typo3_hint_lookup`** for each subsystem in scope, with its concrete paths.
One query per subsystem. A single broad query is not subsystem evidence.
Where step 3 ran with those paths, its answer says whether you still owe this
step. A brief that carried everything the lookup matched says so: "these are
everything typo3_hint_lookup matches for these paths". There the guide made
the call, and the same query returns the same hints. A brief that stopped
short says that instead and names the ids it left. You owe those: fetch them
by id rather than repeat the query.
Read the sentence rather than the populated `hints` key. That key is present
either way and does not tell the two apart. `omittedHints` is that sentence
as data. It is empty where the brief carried everything, and it holds the ids
the brief left where it stopped short.
5. **`typo3_changelog_lookup` with `type: deprecation`**, at each major the
package declares. Omit the query and raise `limit` to carry that major whole.
Those two are the changelog's own axes, and the extension's vocabulary is not
among them. An entry carries a query only when its title carries every word
of it at once. The core titled those entries about its own code.
That is one call per declared major, and what comes back is the major. Every
entry carries its own index tags. `ext:core`, `ext:frontend`, `ext:form` and
the rest name the system extension a change is **in**. `TCA`, `TypoScript`,
`Fluid`, `YAML`, `Backend`, `Frontend` name the surface.
Step 2 picks the package's entries out of that answer by those tags. The tags
are the system extensions it requires, renders through or registers into, and
the kinds of file it ships. That costs no further call. An extension key of
your own is not among them and matches nothing. `tag` narrows one question
inside a major rather than composes the sweep out of eleven.
You check the answers against step 2, which is the other half the words did.
Verify each identifier that comes back in the checkout. A deprecation nothing
here calls is not a finding.
Carry the `FullyScanned` / `PartiallyScanned` tag into the answer. It says
whether the Extension Scanner can find the remaining call sites or whether
that reading is yours. Bounded this way, you can write the sweep before you
open a file. That is why it is a step of the order rather than something the
reading stumbles into.
**What its silence is worth.** A changelog records change events. So a
pattern nothing has touched for ten majors has no entry at all. An empty
sweep is therefore not an answer about what still works. "Does this still
work in version N" goes to `typo3_documentation_lookup` at that version. Ask
it here, and whenever the reading raises it again.
That is a question for a documented surface: a ViewHelper, a TCA type, a
TypoScript setting. The manual matches page titles, section paths and what
each manual declares by name, never the text of a page. Declared is a
property, a class or method the manual documents, a console command. You
reach one by its own name where the query writes that name the way code
does. You also reach it where the query is nothing but the name. A PHP
identifier the manual does not declare has no page named after it.
An identifier goes to `typo3_changelog_lookup` under its own name. That
reaches the entries that write it, however the core titled the change. Then
it goes to the class below. Where the manual has no page for a surface
either, that is a result and not an answer. Undocumented is not unsupported.
**A second declared major.** A package that declares more than one asks a
second question of every deprecation the sweep returns. Is the replacement on
the lower one? The entry's `issue` is a query of its own, and it reaches
every entry filed under that number. The Feature the core announced the
replacement in is among them.
The version the core released it in settles that question. Where the number
reaches no sibling, nobody wrote an entry for the replacement.
`typo3_rule_lookup` with
`documentId="extension/compatibility/a-declared-major-that-is-not-installed"`
is the reading that closes it.
**Where you do not owe the sweep.** A task that produces no change does not
reach this step at all. The property is what the task produces. A triage, a
reproduction and a review illustrate it; they are not the list you read it
off. The sweep asks what a package will have to stop calling. A task that
writes nothing is not going to call anything.
The exemption ends where the workflow produces a change. A review asked to
make the change is that other workflow. It starts this order again with the
files it is about to write. To carry somebody else's patch onto current code
is on the same side. It writes commits. The sweep says whether the code that
moved under the patch deprecated something the patch calls.
Skip the sweep only where the change touches no TYPO3 API: a code style
fixer, a CI file, an `.editorconfig`. A deprecation is a statement about API
the package calls. So a change that calls none has nothing for the sweep to
land on. The sweep is empty before it runs.
That condition is worth a statement, because this step is the largest answer
the order asks for. It is one call per declared major, with that major's
deprecations whole. You read which side a change falls on off the files it
touches, never off the task it started as. One PHP file edited along the way
puts it back among the ordinary ones.
A skip there costs the deprecation no finding would have walked into. How
small the change is decides nothing either. Three statements can call a
deprecated API as easily as three hundred.
A test file is one of those wherever it sits. It calls the API it exercises
and the framework around it, and both deprecate. A fixture is exempt where it
is data the suite reads, and not where it is a class.
**Before the reading**, write down what the order established. That is the
version that filters every later answer, the packages in scope, and the commands
this repository declares. Write down which steps what discharged. Those are
answers already in the session rather than a second reading. What the files show
belongs to the report at the other end. A caller who cannot see what an answer
rests on cannot tell it from one that rests on nothing.
**Then** read the checkout. Not before. A file list first makes everything after
the list look optional. The conventions then arrive as a footnote to a verdict
that has already formed.
**Before the first edit**, name the files this change will create, change or
delete. A deletion is the caller's to ask for, and this is somebody else's
checkout. It is the one act nothing here can put back.
**Last**, the report names every step of this order it did not reach, and what
stood in for it. That is an answer already in the session, a condition that made
the step empty, or an exemption. A reader cannot tell a step passed over in
silence from one somebody dropped.
## When the lookups run out
A behaviour question that survives the lookups above is one you read out of the
installed source. Do not guess at it. The class that implements the behaviour
and the one it inherits from answer it. That reading is the step after the
lookups. It replaces a change to the code until it works.
A first change that did not work is evidence about the reading. So the second
attempt at one failure reads the source rather than changes the code again.
What it settles is what this installation does and never what TYPO3 supports. So
a finding says that you could not settle the question beyond the version
installed. An answer built on the reading names the version it holds for.
## What each runtime lookup adds after the extension answer
`typo3_extension_describe` in step 2 says what one package registers. The
lookups below say what the installation resolved. That is a different fact even
where the words are the same. So step 2 has made none of these calls:
- `typo3_backend_module_lookup` — the tree position, the labels, the access
level, the routes and the navigation component the parent module supplies.
Step 2 lists the modules the package declares. A declaration cannot show that
inheritance.
- `typo3_icon_lookup` — whether any installed package registers an identifier.
That validates the ones a template uses. Step 2 lists the identifiers this
package contributes.
- `typo3_label_lookup` — the labels as the installation resolves them, with its
overrides applied. Step 2 lists the package's XLF files and the source
language each declares, never what a unit says here.
- `typo3_fluid_namespace_list` — the prefixes any template may use without a
declaration, from every package at once. Step 2 lists the package's own
declarations. So an empty list there is no evidence that no package registers
a prefix globally.
- `typo3_configuration_lookup` — the resolved configuration value, after every
extension has had its say. For a form data group it gives the order the
providers really run in. Step 2 answers nothing about that surface at all.
What a registration declares is not what the installation resolves.
- `typo3_service_lookup` — the class the container really injects for a service
id, an interface or a tag. Decorations and overrides count. Step 2 lists what
the package's own `Services.yaml` declares, never what won.
- `typo3_schema_lookup` — the columns TYPO3 derives for a table from its TCA. It
gives the type, the nullability and the default each one gets. Step 2 lists
the tables the package registers and nothing about their shape.
- `typo3_flexform_lookup` — the data structure the installation resolves a
`type=flex` field to, sheet by sheet, with listeners and migrations applied.
Step 2 lists the content elements a package registers, never the structure
each one's form builds.
- `typo3_record_lookup` — the rows of any table the installation has TCA for. It
says how many there are and where they sit. It says what one column holds
across them and which rows depart from its default. Step 2 has no row in its
answer at all.
None of these says whether what it reports is right. `typo3_hint_lookup` and
`typo3_documentation_lookup` do. A subsystem its own runtime lookup confirmed
can still break every rule that governs it. So it is not established until you
asked both.
## A rule reads in both directions
It says what new code should do, and it says what this checkout already does
wrong. A file that settled into the opposite of a rule is a finding, not a local
style to preserve. Consistency with a project's own habit establishes nothing
about whether the habit is right.
## What the code is for is evidence, and the repository states it
A mechanism that costs something is not a defect because it costs. Before you
report one, find what it is there for and say so. That is the manual, the
README, the changelog, the setting that drives it, or the declared versions.
Where the documentation states a purpose, what you have is a trade-off, not a
defect. Name it with its cost and its alternative. Where you cannot find one,
the finding says that you could not establish one, not that none exists. If you
skip this, your review is a list of everything the author did on purpose.
## What a finding rests on is part of the finding
Three things carry one. A file you read, at its path and its line. A command you
ran, with what it printed. A mechanism you traced into an installed package. Say
which of the three it is. If you leave it unsaid, a finding from a CI file
weighs as much as one with a verified line.
Where one of the project's own commands would settle it, run it.
`typo3_project_describe` marks each command it lists **check**, **change** or
**unknown**, read off the declared body. A check reports and hands the code back
as it was. So even a task told not to change files runs it. The linter the
repository already declares is the cheapest evidence in it.
Do not run a change under that instruction. Name an unknown in the answer as
evidence that is available, and do not run it unasked. An unknown is a test
suite, a shell pipeline, a console command.
What a check prints is not the finding. The configuration that makes it fail is
still what the finding is about. The run takes that finding from derived to
established.
## What this server does not know
It does not read your working tree. You establish which files changed, which
branch you are on, and whether a path or an identifier still exists there. Then
pass the concrete paths back, because that turns a general convention into an
answer about this code.
## Query it in English
The knowledge is English and the match is lexical. So a query in another
language reaches the loanwords the two happen to share and nothing else.
Translate the subject before the call and the answer back afterwards, whatever
language you speak with the user.
markdown
# Where every task starts
## Nothing starts until the server answers
A skill is a file the installer left behind. It loads and reads the same whether
the tools behind it are there or not, and neither side notices. So the first
call below is also the check.
- A client may carry this server's name in each tool's name:
`mcp__<server>__typo3_project_describe`. So a search for the bare name comes
back empty where the server is there. A search for a tool's schema needs the
same form. A `select:` on the bare names returns nothing where the tools are
there. Look for the qualified form before you read an empty result as an
answer about the server.
- No `typo3_` tool in this session, or a first call that errors: stop. Say that
this workflow needs the server and it is not there, and name what came back.
- Do not fall back to general TYPO3 knowledge, and do not start to read the
checkout. That answer carries this workflow's order and confidence and none of
its evidence. Nothing in it says which of the two it is.
- Continue only when the user asks you to after you said so. Repeat it in the
answer and in every finding a lookup would have carried.
## The order
This is an order rather than a list. Each step decides what the next one is
worth. Where a step below carries a condition to skip it, that condition is
narrow on purpose. A skipped prescription teaches the next reader to skip the
ones that matter too.
1. **`typo3_project_describe`** — the repository and whether it holds an
installation yet. It reports the TYPO3 and PHP version, the project's own
extensions, its sites, and the commands this repository declares. That
version filters every later answer. A check the repository does not declare
is a wrong answer however sensible it sounds.
The answer ends with the whole procedures this server carries, as ids. That
list is the only place a client that renders no resource list sees their
names. Each one is a `typo3_rule_lookup` with that `documentId` rather than a
search.
2. **`typo3_extension_describe`** for each extension in scope. It says what the
extension registers, and what it ships beside that. That is its manual, its
README, its test layers, and its XLF files with the source language each one
declares. It also says what the extension does *not* ship, and that is the
half no file listing gives you.
Where step 1 reported no extension, that answer is this step, and there is
nothing to call. Say so. A core checkout is that case, because step 1 names
the project's own extensions and not TYPO3's.
3. **`typo3_task_guide`** with a short English task, the paths it touches, the
target version and the change type. It answers the workflow this task belongs
to and the checks that come with it.
Run it in every session, this skill's own tasks included. The guide builds
the brief from the paths as well as the task text. No skill knows which paths
the caller holds.
A skill that covers the task is not that brief. A skipped step costs the
hints and the core checks those paths match. Where the guide's own answer
named this skill, this is one call for an answer already in the session. The
price of a step there is nothing to decide about.
4. **`typo3_hint_lookup`** for each subsystem in scope, with its concrete paths.
One query per subsystem. A single broad query is not subsystem evidence.
Where step 3 ran with those paths, its answer says whether you still owe this
step. A brief that carried everything the lookup matched says so: "these are
everything typo3_hint_lookup matches for these paths". There the guide made
the call, and the same query returns the same hints. A brief that stopped
short says that instead and names the ids it left. You owe those: fetch them
by id rather than repeat the query.
Read the sentence rather than the populated `hints` key. That key is present
either way and does not tell the two apart. `omittedHints` is that sentence
as data. It is empty where the brief carried everything, and it holds the ids
the brief left where it stopped short.
5. **`typo3_changelog_lookup` with `type: deprecation`**, at each major the
package declares. Omit the query and raise `limit` to carry that major whole.
Those two are the changelog's own axes, and the extension's vocabulary is not
among them. An entry carries a query only when its title carries every word
of it at once. The core titled those entries about its own code.
That is one call per declared major, and what comes back is the major. Every
entry carries its own index tags. `ext:core`, `ext:frontend`, `ext:form` and
the rest name the system extension a change is **in**. `TCA`, `TypoScript`,
`Fluid`, `YAML`, `Backend`, `Frontend` name the surface.
Step 2 picks the package's entries out of that answer by those tags. The tags
are the system extensions it requires, renders through or registers into, and
the kinds of file it ships. That costs no further call. An extension key of
your own is not among them and matches nothing. `tag` narrows one question
inside a major rather than composes the sweep out of eleven.
You check the answers against step 2, which is the other half the words did.
Verify each identifier that comes back in the checkout. A deprecation nothing
here calls is not a finding.
Carry the `FullyScanned` / `PartiallyScanned` tag into the answer. It says
whether the Extension Scanner can find the remaining call sites or whether
that reading is yours. Bounded this way, you can write the sweep before you
open a file. That is why it is a step of the order rather than something the
reading stumbles into.
**What its silence is worth.** A changelog records change events. So a
pattern nothing has touched for ten majors has no entry at all. An empty
sweep is therefore not an answer about what still works. "Does this still
work in version N" goes to `typo3_documentation_lookup` at that version. Ask
it here, and whenever the reading raises it again.
That is a question for a documented surface: a ViewHelper, a TCA type, a
TypoScript setting. The manual matches page titles, section paths and what
each manual declares by name, never the text of a page. Declared is a
property, a class or method the manual documents, a console command. You
reach one by its own name where the query writes that name the way code
does. You also reach it where the query is nothing but the name. A PHP
identifier the manual does not declare has no page named after it.
An identifier goes to `typo3_changelog_lookup` under its own name. That
reaches the entries that write it, however the core titled the change. Then
it goes to the class below. Where the manual has no page for a surface
either, that is a result and not an answer. Undocumented is not unsupported.
**A second declared major.** A package that declares more than one asks a
second question of every deprecation the sweep returns. Is the replacement on
the lower one? The entry's `issue` is a query of its own, and it reaches
every entry filed under that number. The Feature the core announced the
replacement in is among them.
The version the core released it in settles that question. Where the number
reaches no sibling, nobody wrote an entry for the replacement.
`typo3_rule_lookup` with
`documentId="extension/compatibility/a-declared-major-that-is-not-installed"`
is the reading that closes it.
**Where you do not owe the sweep.** A task that produces no change does not
reach this step at all. The property is what the task produces. A triage, a
reproduction and a review illustrate it; they are not the list you read it
off. The sweep asks what a package will have to stop calling. A task that
writes nothing is not going to call anything.
The exemption ends where the workflow produces a change. A review asked to
make the change is that other workflow. It starts this order again with the
files it is about to write. To carry somebody else's patch onto current code
is on the same side. It writes commits. The sweep says whether the code that
moved under the patch deprecated something the patch calls.
Skip the sweep only where the change touches no TYPO3 API: a code style
fixer, a CI file, an `.editorconfig`. A deprecation is a statement about API
the package calls. So a change that calls none has nothing for the sweep to
land on. The sweep is empty before it runs.
That condition is worth a statement, because this step is the largest answer
the order asks for. It is one call per declared major, with that major's
deprecations whole. You read which side a change falls on off the files it
touches, never off the task it started as. One PHP file edited along the way
puts it back among the ordinary ones.
A skip there costs the deprecation no finding would have walked into. How
small the change is decides nothing either. Three statements can call a
deprecated API as easily as three hundred.
A test file is one of those wherever it sits. It calls the API it exercises
and the framework around it, and both deprecate. A fixture is exempt where it
is data the suite reads, and not where it is a class.
**Before the reading**, write down what the order established. That is the
version that filters every later answer, the packages in scope, and the commands
this repository declares. Write down which steps what discharged. Those are
answers already in the session rather than a second reading. What the files show
belongs to the report at the other end. A caller who cannot see what an answer
rests on cannot tell it from one that rests on nothing.
**Then** read the checkout. Not before. A file list first makes everything after
the list look optional. The conventions then arrive as a footnote to a verdict
that has already formed.
**Before the first edit**, name the files this change will create, change or
delete. A deletion is the caller's to ask for, and this is somebody else's
checkout. It is the one act nothing here can put back.
**Last**, the report names every step of this order it did not reach, and what
stood in for it. That is an answer already in the session, a condition that made
the step empty, or an exemption. A reader cannot tell a step passed over in
silence from one somebody dropped.
## When the lookups run out
A behaviour question that survives the lookups above is one you read out of the
installed source. Do not guess at it. The class that implements the behaviour
and the one it inherits from answer it. That reading is the step after the
lookups. It replaces a change to the code until it works.
A first change that did not work is evidence about the reading. So the second
attempt at one failure reads the source rather than changes the code again.
What it settles is what this installation does and never what TYPO3 supports. So
a finding says that you could not settle the question beyond the version
installed. An answer built on the reading names the version it holds for.
## What each runtime lookup adds after the extension answer
`typo3_extension_describe` in step 2 says what one package registers. The
lookups below say what the installation resolved. That is a different fact even
where the words are the same. So step 2 has made none of these calls:
- `typo3_backend_module_lookup` — the tree position, the labels, the access
level, the routes and the navigation component the parent module supplies.
Step 2 lists the modules the package declares. A declaration cannot show that
inheritance.
- `typo3_icon_lookup` — whether any installed package registers an identifier.
That validates the ones a template uses. Step 2 lists the identifiers this
package contributes.
- `typo3_label_lookup` — the labels as the installation resolves them, with its
overrides applied. Step 2 lists the package's XLF files and the source
language each declares, never what a unit says here.
- `typo3_fluid_namespace_list` — the prefixes any template may use without a
declaration, from every package at once. Step 2 lists the package's own
declarations. So an empty list there is no evidence that no package registers
a prefix globally.
- `typo3_configuration_lookup` — the resolved configuration value, after every
extension has had its say. For a form data group it gives the order the
providers really run in. Step 2 answers nothing about that surface at all.
What a registration declares is not what the installation resolves.
- `typo3_service_lookup` — the class the container really injects for a service
id, an interface or a tag. Decorations and overrides count. Step 2 lists what
the package's own `Services.yaml` declares, never what won.
- `typo3_schema_lookup` — the columns TYPO3 derives for a table from its TCA. It
gives the type, the nullability and the default each one gets. Step 2 lists
the tables the package registers and nothing about their shape.
- `typo3_flexform_lookup` — the data structure the installation resolves a
`type=flex` field to, sheet by sheet, with listeners and migrations applied.
Step 2 lists the content elements a package registers, never the structure
each one's form builds.
- `typo3_record_lookup` — the rows of any table the installation has TCA for. It
says how many there are and where they sit. It says what one column holds
across them and which rows depart from its default. Step 2 has no row in its
answer at all.
None of these says whether what it reports is right. `typo3_hint_lookup` and
`typo3_documentation_lookup` do. A subsystem its own runtime lookup confirmed
can still break every rule that governs it. So it is not established until you
asked both.
## A rule reads in both directions
It says what new code should do, and it says what this checkout already does
wrong. A file that settled into the opposite of a rule is a finding, not a local
style to preserve. Consistency with a project's own habit establishes nothing
about whether the habit is right.
## What the code is for is evidence, and the repository states it
A mechanism that costs something is not a defect because it costs. Before you
report one, find what it is there for and say so. That is the manual, the
README, the changelog, the setting that drives it, or the declared versions.
Where the documentation states a purpose, what you have is a trade-off, not a
defect. Name it with its cost and its alternative. Where you cannot find one,
the finding says that you could not establish one, not that none exists. If you
skip this, your review is a list of everything the author did on purpose.
## What a finding rests on is part of the finding
Three things carry one. A file you read, at its path and its line. A command you
ran, with what it printed. A mechanism you traced into an installed package. Say
which of the three it is. If you leave it unsaid, a finding from a CI file
weighs as much as one with a verified line.
Where one of the project's own commands would settle it, run it.
`typo3_project_describe` marks each command it lists **check**, **change** or
**unknown**, read off the declared body. A check reports and hands the code back
as it was. So even a task told not to change files runs it. The linter the
repository already declares is the cheapest evidence in it.
Do not run a change under that instruction. Name an unknown in the answer as
evidence that is available, and do not run it unasked. An unknown is a test
suite, a shell pipeline, a console command.
What a check prints is not the finding. The configuration that makes it fail is
still what the finding is about. The run takes that finding from derived to
established.
## What this server does not know
It does not read your working tree. You establish which files changed, which
branch you are on, and whether a path or an identifier still exists there. Then
pass the concrete paths back, because that turns a general convention into an
answer about this code.
## Query it in English
The knowledge is English and the match is lexical. So a query in another
language reaches the loanwords the two happen to share and nothing else.
Translate the subject before the call and the answer back afterwards, whatever
language you speak with the user.