Source for the "TYPO3 Explained" manual, rendered to docs.typo3.org.
Lives in the git repo TYPO3-Documentation/TYPO3CMS-Reference-CoreApi — a
legacy name that doesn't match the manual title (see
Documentation/Index.rst/About.rst). main targets the next TYPO3
major version.
Copy this skeleton and fill it in. These trailers are required on every commit
in this repository, and they take precedence over any personal commit
convention an agent carries from a user-level CLAUDE.md or similar:
[TASK] Imperative one-line summary
Why the change is needed. The diff shows what changed.
Releases: main, 14.3
Assisted-by: Some AI Model <noreply@someai.com>
Signed-off-by: Firstname Lastname <email>
- Prefix: a commit that documents a change in TYPO3 Core copies the subject of
that change including its tags, for example
[!!!][TASK] Use stronger cryptographic algorithm for HMAC. The[!!!]marker belongs to the TYPO3 Core commit rules and only ever arrives here as part of a copied subject. A subject of our own takes[DOCS]for documentation of something the manual did not cover yet,[TASK]for a cleanup, a style fix or tooling, and[BUGFIX]for a correction of wrong content. Releases:is mandatory. Defaultmain, 14.3; a change toAGENTS.mdorCLAUDE.mdtakesmain, 14.3, 13.4. Read rule 4 before adding13.4to any other change.Assisted-by:whenever AI helped draft the commit, in exactly the shape above: the model's own name and version, then a contact address. Replace the name in the skeleton with the model that is actually writing this commit — the skeleton shows one example, and leaving it in place files a false attribution. Do not substitute a slug such asvendor-tool:model-idfrom a personal configuration.Signed-off-by:names the human author.- Keep the trailers in the order shown. Rules 4 and 5 below give the reasoning.
Documentation/ # the manual (reST source, published to docs.typo3.org)
Documentation/_Examples/my_extension/ # example extension, installed and tested
Tests/Functional/ # functional tests of the example extension
.Build/vendor/typo3/cms-* # composer-installed TYPO3 Core, for verifying facts against real source
Makefile # local install/build/test commands
make install— install dependencies. Also re-run after switching to a branch targeting a different TYPO3 version (main/14.3/13.4each pin a different Core version under.Build/vendor) — otherwise you're checking facts against the wrong version.make docs— render the manual locally with Docker.make test-docs— render in minimal-test mode (the same validation CI runs); use this to validate any change before committing.docs-teststill works as a deprecated alias for the target's former name.make test-typoscript— check the.typoscriptand.tsconfigsnippets for syntax errors. TypoScript drops whatever it cannot parse without complaining, so a broken example stays invisible until somebody copies it; the check round trips each file through the Core tokenizer and counts the block braces. A file that demonstrates invalid syntax on purpose opts out with# typoscript-lint: ignore-filein its first line.make test-json— check the.jsonsnippets for syntax errors. JSON has no comment syntax, so an excerpt cannot mark what it leaves out and still has to be a well formed document: wrap the part you show in the braces it belongs in and put(excerpt)in the caption. To show that more members follow, write a"...": "..."member, which is ordinary JSON.make test-rst-style— check the two indentation rules of the style guide that can be decided without reading the page: two spaces after.., and directive options four spaces deeper than their directive. Everything else reST indents has its own width, so a general rule would report noise instead of findings. The body of a literal directive is left alone, andDocumentation/CodeSnippets/is skipped entirely, because a generator writes those files and decides their format.make test-editorconfig— check indentation and whitespace against.editorconfig. It runs only over code files:.editorconfig-checker.jsonexcludes reST, because the checker cannot tell a directive body from an embedded code block and would flag every snippet whose indentation is not a multiple of four.max_line_lengthis disabled there too — it stays in.editorconfigas advice for editors, not as a gate, since reflowing prose is an editorial decision. Add an exclude for a file whose indentation is content rather than formatting, the way.plantumlis excluded because every leading space shifts the rendered note label, and.diffbecause a leading space is part of the patch. A TypoScript multiline value keeps its indentation in the value as well. Never reformat an example whose subject is its own formatting, and check what a file's whitespace actually does before touching it.make test-extension— run the functional tests inTests/Functional/against the example extension inDocumentation/_Examples/my_extension/, on SQLite.make test-phpstan— run PHPStan on the example extension and its tests, on level 8. A test only covers the paths it runs, PHPStan also finds a wrong method name or a removed Core API in the others. Level 9 would need a cast for every value of type mixed, such as the argument of a command, which makes an example harder to read.make test— full test suite (docs, lint, example extension, PHPStan, cgl, yaml, typoscript, json, reST style, editorconfig).make screenshots— set up a TYPO3 instance with SQLite in containers and take the screenshots intoDocumentation/Images/AutomaticScreenshots/;Build/Scripts/runTests.sh -s screenshots AdminTools/UpgradeToolstakes only the named ones.Build/Screenshots/screenshots.mjslists the screenshots it can take. Run it on each branch, so the screenshots show that version.
-
Do not guess a rendered path or an anchor. Every manual publishes its object inventory, so the target of a
:ref:and the page a section sits on can be looked up in one call:curl -s https://docs.typo3.org/m/typo3/reference-coreapi/main/en-us/objects.inv.json \ | jq -r '."std:label" | to_entries[] | select(.key|test("base-variants"; "i")) | "\(.key) -> \(.value[2])"'
std:docmaps document names the same way. Swapping the base URL answers the same question for any other manual, which is how a moved page or the right target for a cross-manual reference is found without rendering. -
Link TYPO3 documentation with permalinks, also inside this manual, and give every link its own link text: https://docs.typo3.org/permalink/h2document:permalinks. Do not suggest replacing a permalink with
:ref:. -
A permalink can be checked without rendering. A
HEADrequest againsthttps://docs.typo3.org/permalink/<interlink>:<anchor>answers307with aLocationnaming the page the anchor resolves to; a wrong anchor answers404:curl -sI https://docs.typo3.org/permalink/t3coreapi:sitehandling-base-variants-functions | grep -Ei '^(HTTP|Location)'
-
A permalink can name a version. Append
@<version>to link the page as it stands on a release branch, for example…:sitehandling-base-variants-functions@13.4. Read theLocationheader to confirm which version answered: an unknown version does not fail, it redirects tomain, so@99.9looks like a working link to the wrong page. The inventory of that version sits at the same URL withmainreplaced,…/reference-coreapi/13.4/en-us/objects.inv.json, which is how an anchor is checked for a branch before a backport links it.
-
Content style: follow the central TYPO3CMS-Guide-HowToDocument guide and its AGENTS.md — sentence case headlines, 4-space indentation (2 spaces after
..), single backticks over double, every headline needs an anchor, etc. Don't duplicate those rules here; if one is missing or wrong, fix it in that repo instead of adding a local workaround.One exception is local to this manual. The guide keeps statements about a version, such as "since TYPO3 v15" or "removed in v15", inside the version directives: https://docs.typo3.org/permalink/h2document:rest-versions-what-belongs-in-directive. A page whose subject is upgrading, such as
ExtensionArchitecture/Extbase/Appendix/Upgrading.rst, names versions in its headlines, tables, and prose by design. Leave those as they are. -
Verify facts — class names, method signatures, whether something is deprecated, when something changed — against
.Build/vendor/typo3/cms-*(incl. itsDocumentation/Changelog/) or real commit history ongithub.com/TYPO3/typo3, not memory. -
Never commit or push without being asked.
-
Backporting: follow the central guide's Backport changes — a
backport <version>label (e.g.backport 14.3) on the PR is what actually triggers the CI runner once it merges, but only repo members can add labels. Always include theReleases:trailer in the commit message regardless of permissions — if you can't add labels yourself, it's the signal a maintainer uses to label the PR (or backport manually) when merging. Unlike the central how-to-document repo (no LTS branches, skips this entirely), this repo hasmain,14.3,13.4(verify this is still current) — default tomain, 14.3; add13.4only for a bugfix/security fix worth backporting that far, not for plain content/style changes. Changes to the agent instruction files (AGENTS.md,CLAUDE.md) always go to all three branches, because agents and review bots also work on the older branches and read that branch's copy. If a PR gets nobackport <version>label, label itmain onlyinstead (mutually exclusive withbackport <version>— not both). Separately, label every PR that addresses aTYPO3-Documentation/Changelog-To-Docissue withchangelog, regardless of its backport status. -
Commit trailers: end every commit message with, in this order — the
Resolves:/References:line for aChangelog-To-Docissue if applicable (see rule 4's changelog note),Releases:, thenAssisted-by: Some AI Model <noreply@someai.com>, naming the model that actually assisted, whenever AI assisted in drafting the commit, then the human author'sSigned-off-by: <Name>. This isn't a DCO requirement tracked elsewhere in this repo — it's an observed convention from merged commit history, so follow the trailer order above rather than only theReleases:/label rules in 4. -
Taking over someone else's commits: this repository merges by squash, so a contributor whose commits you carry into your own branch survives on the target branch only as a
Co-authored-by:trailer in the squash message. GitHub writes those trailers itself, because the squash message is composed from the commit messages — PR #7017 reachedmainwithCo-authored-by: Sarah McCarthythat way. They are lost when the person merging replaces the proposed message, so keep the generatedCo-authored-by:lines when editing it, and add a missing one by hand. -
Merging the bot's backport PRs: once a labelled PR is merged,
typo3-docs-backport-botopens the[Backport <version>]PRs. Afterwards confirm the commit actually reached the release branch (git log origin/14.3 --grep=...): abackport-donelabel is not proof, and a backport can fail without opening a pull request at all, leaving the branch behind with nothing to notice. When several backports touch the same file, merge them in the order their originals landed onmain, and letmergeable_stateleaveunknownbefore merging the next one — GitHub recomputes it for a minute or two after a merge moved the base. -
Example extension: a complete example that does something the manual makes claims about belongs in the example extension in
Documentation/_Examples/my_extension/(myvendor/my-extension), with a functional test inTests/Functional/that runs it and checks the result the text describes. The page includes the file with.. literalinclude:: /_Examples/my_extension/.... Every class in the extension is loaded at the same time, so give each one a name that says what it does, and give every command, identifier, and table its own name. Fragments, variants of one class, and examples that show wrong code on purpose stay in_*.phpfiles next to their page. The extension is installed through apathrepository, so runmake installafter changing itscomposer.json.
- TYPO3CMS-Guide-HowToDocument — content style & RST conventions
- AGENTS.md — shared agent rules for TYPO3-Documentation repos