{"id":64,"date":"2026-10-17T15:00:00","date_gmt":"2026-10-17T15:00:00","guid":{"rendered":"https:\/\/myport.ddev.site\/blog\/letting-an-ai-agent-upgrade-my-typo3-site-with-tests-in-charge\/"},"modified":"2026-10-16T12:00:12","modified_gmt":"2026-10-16T12:00:12","slug":"letting-an-ai-agent-upgrade-my-typo3-site-with-tests-in-charge","status":"publish","type":"post","link":"https:\/\/hoja.in\/blog\/letting-an-ai-agent-upgrade-my-typo3-site-with-tests-in-charge\/","title":{"rendered":"Letting an AI agent upgrade my TYPO3 site, with tests in charge"},"content":{"rendered":"<p>When I moved my own site hoja.in from TYPO3 7.6 to 14.3 this year, I did not type most of the commands myself. Claude Code did, with a project-migration plugin that I use for TYPO3 upgrades. It made the inventory, restructured the repository into a Composer project, wrote the content import script and the new site package, and upgraded the WordPress blog next to it.<\/p>\n<p>That sounds like a marketing sentence, so let me say the important part first: the agent did not decide when something was done. A smoke baseline, a verification gate and my own review decided. This post is about that arrangement, and about the places where the agent was wrong.<\/p>\n<h2>Why I did not just say &#8220;upgrade my site&#8221;<\/h2>\n<p>AI output looks plausible. That is the problem. An agent can write a perfect-looking summary (&#8220;all pages render correctly&#8221;) after checking two URLs, or after checking none. I have seen the same pattern in human pull requests too, to be fair. So before any code changed, I wanted three things that do not depend on anybody&#8217;s confidence:<\/p>\n<ol>\n<li>A <strong>baseline<\/strong>: what the old site really delivered, measured.<\/li>\n<li>A <strong>gate<\/strong>: a fixed list of checks that must pass, with real output.<\/li>\n<li>A <strong>ledger<\/strong>: one line per finding, with a status, which the agent must keep up to date and which I can read in two minutes.<\/li>\n<\/ol>\n<h2>Step 1: The baseline<\/h2>\n<p>The agent booted the old 7.6 site in a separate DDEV project with PHP 7.0 and the live database dump, then captured seven URLs: status, final URL, bytes, page title and an error marker flag. This is the real file, shortened:<\/p>\n<pre><code class=\"language-text\">url                                            status  bytes  title                                    error\nhttps:\/\/myport-legacy.ddev.site\/               200     55793  hoja.in | TYPO3 Certified Web Developer  no\nhttps:\/\/myport-legacy.ddev.site\/index.php?id=7 200     60887  About                                    no\nhttps:\/\/myport-legacy.ddev.site\/index.php?id=13 200    62851  Contact                                  no<\/code><\/pre>\n<p>Even this step had a surprise. The committed 7.6 core had an empty <code>index.php<\/code>. The agent found it, took the file from the original <code>typo3_src-7.6.10.zip<\/code>, and wrote it into the ledger instead of quietly &#8220;fixing&#8221; the repository. That is the behaviour I want.<\/p>\n<h2>Step 2: The gate<\/h2>\n<p>The plugin has a verify step that runs the project gate. In plain commands, the gate for this project looks like this:<\/p>\n<pre><code class=\"language-bash\">ddev composer show typo3\/cms-core | grep versions   # really 14.3.x?\nddev composer validate\nddev composer check-platform-reqs\nddev exec vendor\/bin\/typo3 cache:flush\nddev exec vendor\/bin\/typo3 upgrade:list              # expect: no wizards left\n# then: the same URLs as the baseline, plus the new ones, compared\n# against the baseline, and new lines in the error log<\/code><\/pre>\n<p>The rule was simple: the agent may say &#8220;done&#8221; only with the gate output in front of it. The final run showed the seven baseline URLs with status 200 and no error markers, plus the new speaking URLs (<code>\/about<\/code>, <code>\/contact<\/code>), <code>\/blog\/<\/code>, one blog post and <code>sitemap.xml<\/code>, all 200. On top of that, a real contact form submission had to arrive in Mailpit, with the visitor as reply-to. It did.<\/p>\n<h2>Step 3: The ledger<\/h2>\n<p>The ledger is a Markdown file with lines like <code>id \u00b7 area \u00b7 status \u00b7 note<\/code>. Status is one of fixed, decision, deferred or open. A few real lines:<\/p>\n<pre><code class=\"language-text\">L04 \u00b7 own-ext \u00b7 decision \u00b7 smslogin dropped (user): no plugin on any page of the live DB\nL11 \u00b7 database \u00b7 fixed \u00b7 Breaking-17406: pages.url removed, blog link page uses pages.link = \/blog\/\nL20 \u00b7 blog \u00b7 deferred \u00b7 no pre-upgrade visual baseline of the blog; check layout by eye\nL23 \u00b7 deploy \u00b7 open \u00b7 production PHP version of the hosting must be &gt;= 8.2<\/code><\/pre>\n<p>Two details made it work. First, the ledger separates <em>my<\/em> decisions from decisions the agent took on its own, which are marked &#8220;open for veto&#8221;. Dropping a cookie bar extension without a v14 release was one of those. Second, &#8220;deferred&#8221; and &#8220;open&#8221; are allowed. An honest open item is much better than a fake &#8220;fixed&#8221;.<\/p>\n<h2>A guard against the worst mistake<\/h2>\n<p>The one thing I could not accept was a lost database. The setup has a hook that blocks database writes when there is no fresh dump. So before the import, before the WordPress database upgrade and before every bigger content change, a dump was written first. The backups folder now has a row of files like <code>pre-v14-import.sql.gz<\/code> and <code>pre-wp-upgrade-blog.sql.gz<\/code>. Each one is a point I can go back to, and that lets me sleep better.<\/p>\n<h2>Where it went wrong<\/h2>\n<p>This is the part most AI success stories skip. The review, done partly by a separate reviewer agent and partly by me, found real problems:<\/p>\n<ul>\n<li><strong>A wrong claim about a script.<\/strong> The plan first said that a third-party chat script loads on the contact page. A closer look showed it sits only in a content element on a hidden page and was never rendered, before or after. The ledger line says &#8220;corrected after review&#8221;.<\/li>\n<li><strong>Wrong page owner.<\/strong> The import first set the CLI user <code>_cli_<\/code> as owner of all pages. Fixed to the real admin.<\/li>\n<li><strong>A config file outside git.<\/strong> DDEV was still managing <code>config\/system\/additional.php<\/code>, so the file that switches between DDEV and production was not in the repository. The reviewer marked it as a blocker; now DDEV&#8217;s settings management is switched off for it.<\/li>\n<li><strong>A secret coming back.<\/strong> After I saved backend settings, TYPO3 wrote an <code>encryptionKey<\/code> into <code>settings.php<\/code> again. The ledger has it as open: move it out before committing.<\/li>\n<li><strong>Smoke &#8220;regressions&#8221; that were not.<\/strong> After the redesign, all seven pages were flagged because the title of the root page changed and the pages got much smaller. Status 200, no errors. That needs <em>my<\/em> acceptance before the baseline is replaced, not the agent&#8217;s.<\/li>\n<\/ul>\n<p>Also, some content was drafted by the agent from my CV and other indirect evidence. It is marked in the ledger as &#8220;owner to review&#8221;. An agent can write a nice &#8220;What I do&#8221; paragraph, but only I know if it is true.<\/p>\n<h2>What I learned<\/h2>\n<ul>\n<li>Give the agent checks, not trust. A baseline and a gate turn &#8220;looks good&#8221; into numbers.<\/li>\n<li>Make it write things down. The ledger was the cheapest review tool I ever had.<\/li>\n<li>Keep decisions with a human. The agent can propose dropping an extension; a person says yes.<\/li>\n<li>Block the dangerous actions technically, not with a polite request in the prompt.<\/li>\n<\/ul>\n<p>Would I do it again? Yes. The agent saved me a lot of typing and searching, and it was honest whenever the setup forced it to be. The setup is what made the difference, not the model.<\/p>\n<p>The deeper posts on the pieces: <a href=\"\/blog\/a-verification-gate-for-ai-written-php-lint-phpstan-tests\/\">a verification gate for AI-written PHP<\/a>, <a href=\"\/blog\/a-claude-code-hook-that-blocks-db-writes-without-a-fresh-dump\/\">the hook that blocks database writes without a dump<\/a> and <a href=\"\/blog\/a-smoke-test-baseline-before-you-touch-a-typo3-upgrade\/\">the smoke test baseline<\/a>.<\/p>\n<h2>Further reading<\/h2>\n<ul>\n<li><a href=\"https:\/\/code.claude.com\/docs\/en\/best-practices\">Claude Code: Best practices<\/a><\/li>\n<li><a href=\"https:\/\/code.claude.com\/docs\/en\/hooks\">Claude Code: Hooks reference<\/a><\/li>\n<li><a href=\"https:\/\/docs.typo3.org\/m\/typo3\/guide-installation\/main\/en-us\/Major\/Index.html\">TYPO3: Major upgrades<\/a><\/li>\n<\/ul>\n","protected":false},"excerpt":{"rendered":"<p>Claude Code did the legwork of my TYPO3 7.6 to 14.3 migration. A smoke baseline, a verification gate, a ledger and human review decided what was really done.<\/p>\n","protected":false},"author":1,"featured_media":65,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[18],"tags":[34,35,31,5,25],"class_list":["post-64","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-ai-engineering","tag-ai-agents","tag-claude-code","tag-testing","tag-typo3","tag-upgrades"],"yoast_head":"<!-- This site is optimized with the Yoast SEO plugin v28.7 - https:\/\/yoast.com\/product\/yoast-seo-wordpress\/ -->\n<title>An AI agent upgraded my TYPO3 site, tests decided<\/title>\n<meta name=\"description\" content=\"How Claude Code with a project-migration plugin moved hoja.in to TYPO3 14.3, and how a smoke baseline, a gate, a ledger and review kept the agent honest.\" \/>\n<meta name=\"robots\" content=\"index, follow, max-snippet:-1, max-image-preview:large, max-video-preview:-1\" \/>\n<link rel=\"canonical\" href=\"https:\/\/hoja.in\/blog\/letting-an-ai-agent-upgrade-my-typo3-site-with-tests-in-charge\/\" \/>\n<meta property=\"og:locale\" content=\"en_US\" \/>\n<meta property=\"og:type\" content=\"article\" \/>\n<meta property=\"og:title\" content=\"An AI agent upgraded my TYPO3 site, tests decided\" \/>\n<meta property=\"og:description\" content=\"How Claude Code with a project-migration plugin moved hoja.in to TYPO3 14.3, and how a smoke baseline, a gate, a ledger and review kept the agent honest.\" \/>\n<meta property=\"og:url\" content=\"https:\/\/hoja.in\/blog\/letting-an-ai-agent-upgrade-my-typo3-site-with-tests-in-charge\/\" \/>\n<meta property=\"og:site_name\" content=\"Hoja Mustaffa Abdul Latheef \u2013 Blog\" \/>\n<meta property=\"article:published_time\" content=\"2026-10-17T15:00:00+00:00\" \/>\n<meta property=\"og:image\" content=\"https:\/\/hoja.in\/blog\/wp-content\/uploads\/2026\/10\/letting-an-ai-agent-upgrade-my-typo3-site-with-tests-in-charge.jpg\" \/>\n\t<meta property=\"og:image:width\" content=\"1200\" \/>\n\t<meta property=\"og:image:height\" content=\"630\" \/>\n\t<meta property=\"og:image:type\" content=\"image\/jpeg\" \/>\n<meta name=\"author\" content=\"Hoja Mustaffa Abdul Latheef\" \/>\n<meta name=\"twitter:card\" content=\"summary_large_image\" \/>\n<meta name=\"twitter:creator\" content=\"@hojaonline\" \/>\n<meta name=\"twitter:site\" content=\"@hojaonline\" \/>\n<meta name=\"twitter:label1\" content=\"Written by\" \/>\n\t<meta name=\"twitter:data1\" content=\"Hoja Mustaffa Abdul Latheef\" \/>\n\t<meta name=\"twitter:label2\" content=\"Est. reading time\" \/>\n\t<meta name=\"twitter:data2\" content=\"6 minutes\" \/>\n<script type=\"application\/ld+json\" class=\"yoast-schema-graph\">{\"@context\":\"https:\\\/\\\/schema.org\",\"@graph\":[{\"@type\":\"Article\",\"@id\":\"https:\\\/\\\/hoja.in\\\/blog\\\/letting-an-ai-agent-upgrade-my-typo3-site-with-tests-in-charge\\\/#article\",\"isPartOf\":{\"@id\":\"https:\\\/\\\/hoja.in\\\/blog\\\/letting-an-ai-agent-upgrade-my-typo3-site-with-tests-in-charge\\\/\"},\"author\":{\"name\":\"Hoja Mustaffa Abdul Latheef\",\"@id\":\"https:\\\/\\\/hoja.in\\\/blog\\\/#\\\/schema\\\/person\\\/e6fbd54c2061674d8ea3e7861d858c7b\"},\"headline\":\"Letting an AI agent upgrade my TYPO3 site, with tests in charge\",\"datePublished\":\"2026-10-17T15:00:00+00:00\",\"mainEntityOfPage\":{\"@id\":\"https:\\\/\\\/hoja.in\\\/blog\\\/letting-an-ai-agent-upgrade-my-typo3-site-with-tests-in-charge\\\/\"},\"wordCount\":956,\"commentCount\":0,\"publisher\":{\"@id\":\"https:\\\/\\\/hoja.in\\\/blog\\\/#\\\/schema\\\/person\\\/e6fbd54c2061674d8ea3e7861d858c7b\"},\"image\":{\"@id\":\"https:\\\/\\\/hoja.in\\\/blog\\\/letting-an-ai-agent-upgrade-my-typo3-site-with-tests-in-charge\\\/#primaryimage\"},\"thumbnailUrl\":\"https:\\\/\\\/hoja.in\\\/blog\\\/wp-content\\\/uploads\\\/2026\\\/10\\\/letting-an-ai-agent-upgrade-my-typo3-site-with-tests-in-charge.jpg\",\"keywords\":[\"AI agents\",\"Claude Code\",\"Testing\",\"TYPO3\",\"Upgrades\"],\"articleSection\":[\"AI engineering\"],\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"CommentAction\",\"name\":\"Comment\",\"target\":[\"https:\\\/\\\/hoja.in\\\/blog\\\/letting-an-ai-agent-upgrade-my-typo3-site-with-tests-in-charge\\\/#respond\"]}]},{\"@type\":\"WebPage\",\"@id\":\"https:\\\/\\\/hoja.in\\\/blog\\\/letting-an-ai-agent-upgrade-my-typo3-site-with-tests-in-charge\\\/\",\"url\":\"https:\\\/\\\/hoja.in\\\/blog\\\/letting-an-ai-agent-upgrade-my-typo3-site-with-tests-in-charge\\\/\",\"name\":\"An AI agent upgraded my TYPO3 site, tests decided\",\"isPartOf\":{\"@id\":\"https:\\\/\\\/hoja.in\\\/blog\\\/#website\"},\"primaryImageOfPage\":{\"@id\":\"https:\\\/\\\/hoja.in\\\/blog\\\/letting-an-ai-agent-upgrade-my-typo3-site-with-tests-in-charge\\\/#primaryimage\"},\"image\":{\"@id\":\"https:\\\/\\\/hoja.in\\\/blog\\\/letting-an-ai-agent-upgrade-my-typo3-site-with-tests-in-charge\\\/#primaryimage\"},\"thumbnailUrl\":\"https:\\\/\\\/hoja.in\\\/blog\\\/wp-content\\\/uploads\\\/2026\\\/10\\\/letting-an-ai-agent-upgrade-my-typo3-site-with-tests-in-charge.jpg\",\"datePublished\":\"2026-10-17T15:00:00+00:00\",\"description\":\"How Claude Code with a project-migration plugin moved hoja.in to TYPO3 14.3, and how a smoke baseline, a gate, a ledger and review kept the agent honest.\",\"breadcrumb\":{\"@id\":\"https:\\\/\\\/hoja.in\\\/blog\\\/letting-an-ai-agent-upgrade-my-typo3-site-with-tests-in-charge\\\/#breadcrumb\"},\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"ReadAction\",\"target\":[\"https:\\\/\\\/hoja.in\\\/blog\\\/letting-an-ai-agent-upgrade-my-typo3-site-with-tests-in-charge\\\/\"]}]},{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\\\/\\\/hoja.in\\\/blog\\\/letting-an-ai-agent-upgrade-my-typo3-site-with-tests-in-charge\\\/#primaryimage\",\"url\":\"https:\\\/\\\/hoja.in\\\/blog\\\/wp-content\\\/uploads\\\/2026\\\/10\\\/letting-an-ai-agent-upgrade-my-typo3-site-with-tests-in-charge.jpg\",\"contentUrl\":\"https:\\\/\\\/hoja.in\\\/blog\\\/wp-content\\\/uploads\\\/2026\\\/10\\\/letting-an-ai-agent-upgrade-my-typo3-site-with-tests-in-charge.jpg\",\"width\":1200,\"height\":630,\"caption\":\"The agent works, tests decide\"},{\"@type\":\"BreadcrumbList\",\"@id\":\"https:\\\/\\\/hoja.in\\\/blog\\\/letting-an-ai-agent-upgrade-my-typo3-site-with-tests-in-charge\\\/#breadcrumb\",\"itemListElement\":[{\"@type\":\"ListItem\",\"position\":1,\"name\":\"Home\",\"item\":\"https:\\\/\\\/hoja.in\\\/blog\\\/\"},{\"@type\":\"ListItem\",\"position\":2,\"name\":\"Letting an AI agent upgrade my TYPO3 site, with tests in charge\"}]},{\"@type\":\"WebSite\",\"@id\":\"https:\\\/\\\/hoja.in\\\/blog\\\/#website\",\"url\":\"https:\\\/\\\/hoja.in\\\/blog\\\/\",\"name\":\"Hoja Mustaffa Abdul Latheef \u2013 Blog\",\"description\":\"Notes from daily work with TYPO3, PHP and AI-assisted engineering\",\"publisher\":{\"@id\":\"https:\\\/\\\/hoja.in\\\/blog\\\/#\\\/schema\\\/person\\\/e6fbd54c2061674d8ea3e7861d858c7b\"},\"potentialAction\":[{\"@type\":\"SearchAction\",\"target\":{\"@type\":\"EntryPoint\",\"urlTemplate\":\"https:\\\/\\\/hoja.in\\\/blog\\\/?s={search_term_string}\"},\"query-input\":{\"@type\":\"PropertyValueSpecification\",\"valueRequired\":true,\"valueName\":\"search_term_string\"}}],\"inLanguage\":\"en-US\"},{\"@type\":[\"Person\",\"Organization\"],\"@id\":\"https:\\\/\\\/hoja.in\\\/blog\\\/#\\\/schema\\\/person\\\/e6fbd54c2061674d8ea3e7861d858c7b\",\"name\":\"Hoja Mustaffa Abdul Latheef\",\"logo\":{\"@id\":\"https:\\\/\\\/hoja.in\\\/blog\\\/#\\\/schema\\\/person\\\/image\\\/\"},\"description\":\"Senior TYPO3 Developer at jweiland.net, TYPO3 CMS Certified Developer. I write about TYPO3 upgrades, extension development, PHP and AI-assisted engineering with Claude Code.\",\"sameAs\":[\"https:\\\/\\\/hoja.in\\\/\"]}]}<\/script>\n<!-- \/ Yoast SEO plugin. -->","yoast_head_json":{"title":"An AI agent upgraded my TYPO3 site, tests decided","description":"How Claude Code with a project-migration plugin moved hoja.in to TYPO3 14.3, and how a smoke baseline, a gate, a ledger and review kept the agent honest.","robots":{"index":"index","follow":"follow","max-snippet":"max-snippet:-1","max-image-preview":"max-image-preview:large","max-video-preview":"max-video-preview:-1"},"canonical":"https:\/\/hoja.in\/blog\/letting-an-ai-agent-upgrade-my-typo3-site-with-tests-in-charge\/","og_locale":"en_US","og_type":"article","og_title":"An AI agent upgraded my TYPO3 site, tests decided","og_description":"How Claude Code with a project-migration plugin moved hoja.in to TYPO3 14.3, and how a smoke baseline, a gate, a ledger and review kept the agent honest.","og_url":"https:\/\/hoja.in\/blog\/letting-an-ai-agent-upgrade-my-typo3-site-with-tests-in-charge\/","og_site_name":"Hoja Mustaffa Abdul Latheef \u2013 Blog","article_published_time":"2026-10-17T15:00:00+00:00","og_image":[{"width":1200,"height":630,"url":"https:\/\/hoja.in\/blog\/wp-content\/uploads\/2026\/10\/letting-an-ai-agent-upgrade-my-typo3-site-with-tests-in-charge.jpg","type":"image\/jpeg"}],"author":"Hoja Mustaffa Abdul Latheef","twitter_card":"summary_large_image","twitter_creator":"@hojaonline","twitter_site":"@hojaonline","twitter_misc":{"Written by":"Hoja Mustaffa Abdul Latheef","Est. reading time":"6 minutes"},"schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":"Article","@id":"https:\/\/hoja.in\/blog\/letting-an-ai-agent-upgrade-my-typo3-site-with-tests-in-charge\/#article","isPartOf":{"@id":"https:\/\/hoja.in\/blog\/letting-an-ai-agent-upgrade-my-typo3-site-with-tests-in-charge\/"},"author":{"name":"Hoja Mustaffa Abdul Latheef","@id":"https:\/\/hoja.in\/blog\/#\/schema\/person\/e6fbd54c2061674d8ea3e7861d858c7b"},"headline":"Letting an AI agent upgrade my TYPO3 site, with tests in charge","datePublished":"2026-10-17T15:00:00+00:00","mainEntityOfPage":{"@id":"https:\/\/hoja.in\/blog\/letting-an-ai-agent-upgrade-my-typo3-site-with-tests-in-charge\/"},"wordCount":956,"commentCount":0,"publisher":{"@id":"https:\/\/hoja.in\/blog\/#\/schema\/person\/e6fbd54c2061674d8ea3e7861d858c7b"},"image":{"@id":"https:\/\/hoja.in\/blog\/letting-an-ai-agent-upgrade-my-typo3-site-with-tests-in-charge\/#primaryimage"},"thumbnailUrl":"https:\/\/hoja.in\/blog\/wp-content\/uploads\/2026\/10\/letting-an-ai-agent-upgrade-my-typo3-site-with-tests-in-charge.jpg","keywords":["AI agents","Claude Code","Testing","TYPO3","Upgrades"],"articleSection":["AI engineering"],"inLanguage":"en-US","potentialAction":[{"@type":"CommentAction","name":"Comment","target":["https:\/\/hoja.in\/blog\/letting-an-ai-agent-upgrade-my-typo3-site-with-tests-in-charge\/#respond"]}]},{"@type":"WebPage","@id":"https:\/\/hoja.in\/blog\/letting-an-ai-agent-upgrade-my-typo3-site-with-tests-in-charge\/","url":"https:\/\/hoja.in\/blog\/letting-an-ai-agent-upgrade-my-typo3-site-with-tests-in-charge\/","name":"An AI agent upgraded my TYPO3 site, tests decided","isPartOf":{"@id":"https:\/\/hoja.in\/blog\/#website"},"primaryImageOfPage":{"@id":"https:\/\/hoja.in\/blog\/letting-an-ai-agent-upgrade-my-typo3-site-with-tests-in-charge\/#primaryimage"},"image":{"@id":"https:\/\/hoja.in\/blog\/letting-an-ai-agent-upgrade-my-typo3-site-with-tests-in-charge\/#primaryimage"},"thumbnailUrl":"https:\/\/hoja.in\/blog\/wp-content\/uploads\/2026\/10\/letting-an-ai-agent-upgrade-my-typo3-site-with-tests-in-charge.jpg","datePublished":"2026-10-17T15:00:00+00:00","description":"How Claude Code with a project-migration plugin moved hoja.in to TYPO3 14.3, and how a smoke baseline, a gate, a ledger and review kept the agent honest.","breadcrumb":{"@id":"https:\/\/hoja.in\/blog\/letting-an-ai-agent-upgrade-my-typo3-site-with-tests-in-charge\/#breadcrumb"},"inLanguage":"en-US","potentialAction":[{"@type":"ReadAction","target":["https:\/\/hoja.in\/blog\/letting-an-ai-agent-upgrade-my-typo3-site-with-tests-in-charge\/"]}]},{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/hoja.in\/blog\/letting-an-ai-agent-upgrade-my-typo3-site-with-tests-in-charge\/#primaryimage","url":"https:\/\/hoja.in\/blog\/wp-content\/uploads\/2026\/10\/letting-an-ai-agent-upgrade-my-typo3-site-with-tests-in-charge.jpg","contentUrl":"https:\/\/hoja.in\/blog\/wp-content\/uploads\/2026\/10\/letting-an-ai-agent-upgrade-my-typo3-site-with-tests-in-charge.jpg","width":1200,"height":630,"caption":"The agent works, tests decide"},{"@type":"BreadcrumbList","@id":"https:\/\/hoja.in\/blog\/letting-an-ai-agent-upgrade-my-typo3-site-with-tests-in-charge\/#breadcrumb","itemListElement":[{"@type":"ListItem","position":1,"name":"Home","item":"https:\/\/hoja.in\/blog\/"},{"@type":"ListItem","position":2,"name":"Letting an AI agent upgrade my TYPO3 site, with tests in charge"}]},{"@type":"WebSite","@id":"https:\/\/hoja.in\/blog\/#website","url":"https:\/\/hoja.in\/blog\/","name":"Hoja Mustaffa Abdul Latheef \u2013 Blog","description":"Notes from daily work with TYPO3, PHP and AI-assisted engineering","publisher":{"@id":"https:\/\/hoja.in\/blog\/#\/schema\/person\/e6fbd54c2061674d8ea3e7861d858c7b"},"potentialAction":[{"@type":"SearchAction","target":{"@type":"EntryPoint","urlTemplate":"https:\/\/hoja.in\/blog\/?s={search_term_string}"},"query-input":{"@type":"PropertyValueSpecification","valueRequired":true,"valueName":"search_term_string"}}],"inLanguage":"en-US"},{"@type":["Person","Organization"],"@id":"https:\/\/hoja.in\/blog\/#\/schema\/person\/e6fbd54c2061674d8ea3e7861d858c7b","name":"Hoja Mustaffa Abdul Latheef","logo":{"@id":"https:\/\/hoja.in\/blog\/#\/schema\/person\/image\/"},"description":"Senior TYPO3 Developer at jweiland.net, TYPO3 CMS Certified Developer. I write about TYPO3 upgrades, extension development, PHP and AI-assisted engineering with Claude Code.","sameAs":["https:\/\/hoja.in\/"]}]}},"_links":{"self":[{"href":"https:\/\/hoja.in\/blog\/wp-json\/wp\/v2\/posts\/64","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/hoja.in\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/hoja.in\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/hoja.in\/blog\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/hoja.in\/blog\/wp-json\/wp\/v2\/comments?post=64"}],"version-history":[{"count":1,"href":"https:\/\/hoja.in\/blog\/wp-json\/wp\/v2\/posts\/64\/revisions"}],"predecessor-version":[{"id":155,"href":"https:\/\/hoja.in\/blog\/wp-json\/wp\/v2\/posts\/64\/revisions\/155"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/hoja.in\/blog\/wp-json\/wp\/v2\/media\/65"}],"wp:attachment":[{"href":"https:\/\/hoja.in\/blog\/wp-json\/wp\/v2\/media?parent=64"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/hoja.in\/blog\/wp-json\/wp\/v2\/categories?post=64"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/hoja.in\/blog\/wp-json\/wp\/v2\/tags?post=64"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}