# Update Log ## Run Metadata - Date: 2026-02-10 - Started: 2026-02-10 12:36:36 CET - Branch: `upgrade/laravel12-filament5-full` - Goal: Laravel 12 + full ecosystem upgrades (Composer and npm), including Filament v3 -> v4 -> v5, while skipping ESLint major upgrades. ## Preflight Snapshot ### Environment - PHP: `8.4.17` - Composer: `2.8.5` - Node: `v20.19.6` - npm: `11.6.4` ### Composer Outdated (Direct) - Laravel major target available: `laravel/framework 11.46.1 -> 12.50.0`. - Filament major target available: `filament/filament 3.3.45 -> 5.2.0`. - Inertia Laravel major target available: `1.3.3 -> 2.0.19`. - Ziggy major target available: `1.8.2 -> 2.6.0`. - Multiple ecosystem majors available (Filament plugins, DOMPDF, Mollie, Pest stack, Symfony components, Resend). ### Laravel 12 Blocker Check (`composer why-not laravel/framework ^12 -t`) - Hard blockers detected: - `codezero/laravel-localized-routes:^4` (supports only Illuminate 10/11). - `codezero/laravel-localizer:^3` (supports only Illuminate 10/11). - `codezero/laravel-uri-translator:^2` (transitive, supports only Illuminate 10/11). - `resend/resend-laravel:^0.14` (supports only Illuminate 10/11). - `laravel-lang` current line (`lang:^12`, `common:^3`) anchored to publisher line that supports only Illuminate < =11. ### npm Outdated Snapshot - Major targets available: - `@inertiajs/vue3 1.3.0 -> 2.3.13` - `tailwindcss 3.4.18 -> 4.1.18` - `laravel-vite-plugin 1.3.0 -> 2.1.0` - `vite 6.4.1 -> 7.3.1` - `@vitejs/plugin-vue 5.2.4 -> 6.0.4` - `pinia 2.3.1 -> 3.0.4` - `swiper 10.3.1 -> 12.1.0` - `vue-i18n 10.0.8 -> 11.2.8` - ESLint majors are available but intentionally skipped in this run. ### Baseline Verification - `php artisan test --compact`: `14 passed`, no failures. - Warnings: PHPUnit doc-comment metadata deprecations (will break in PHPUnit 12 timeframe). - `npm run build`: build succeeds. - Warning: `baseline-browser-mapping` database is older than two months. ## Package Blockers - `codezero/laravel-localizer` removed per decision (no Laravel 12 support). - `codezero/laravel-localized-routes` replaced by `opgginc/codezero-laravel-localized-routes` (Laravel 12 support). - `resend/resend-laravel` must move to `^1.0`. - `laravel-lang/lang` and `laravel-lang/common` must move to newer major lines compatible with Laravel 12. ## Exceptions & Weirdness 1. **Stale package/service manifests after Composer package changes** - Symptom: runtime errors for removed/moved service providers (for example debugbar, old localizer, filament-upgrade provider references). - Failing commands: `php artisan ...` commands after `composer update --no-scripts`. - Root cause: stale `bootstrap/cache/packages.php` and `bootstrap/cache/services.php`. - Exact fix: delete both files and re-run `composer dump-autoload` / package discovery. - Preventive rule: clear both manifest files after major package removals/replacements. 2. **Localized route transition edge case (`Attribute [localized] does not exist`)** - Symptom: route boot failure during transition from abandoned CodeZero packages to OPGG fork. - Root cause: cached package manifests + package transition timing. - Exact fix: same manifest clear as above, then rerun discovery. 3. **Ziggy v2 client dependency gap** - Symptom: Vite build failed with `Rollup failed to resolve import "qs-esm"` from `vendor/tightenco/ziggy/src/js/Route.js`. - Failing command: `npm run build`. - Root cause: Ziggy v2 JS source now imports `qs-esm`, not present in project npm deps. - Exact fix: `npm install qs-esm`. - Preventive rule: after Ziggy major upgrades, run a full build immediately and verify vendor JS imports. 4. **PHPUnit 12 test discovery change reduced executed test count (14 -> 3)** - Symptom: only methods named `test_*` executed; legacy `/** @test */` methods were skipped. - Failing command: `php artisan test --compact` (unexpected low count). - Root cause: metadata annotation behavior changed in PHPUnit 12 line. - Exact fix: migrate `/** @test */` methods to `#[Test]` attributes in affected class-based tests. - Preventive rule: include a test-count sanity check after PHPUnit major upgrades. 5. **Hidden regression surfaced after restoring full test discovery** - Symptom: `Call to undefined function get_classes_in_namespace()` in sitemap generation test. - Failing command: `php artisan test --compact`. - Root cause: helper function no longer provided by dependencies after package upgrades. - Exact fix: added local `get_classes_in_namespace()` implementation to `app/helpers.php`. - Preventive rule: once discovery is fixed, rerun full suite and treat newly exposed failures as real upgrade regressions. 6. **Pint interaction with PHPUnit setup visibility** - Symptom: `setUp()` visibility conflict (`must be public`) after formatting pass. - Failing command: `php artisan test --compact`. - Root cause: formatter rule normalized setup method visibility in a way that conflicted with this TestCase chain. - Exact fix: remove no-op `setUp()` overrides from affected tests. - Preventive rule: rerun tests after Pint in upgrade branches, not just before Pint. 7. **npm package location warning due duplicate entries** - Symptom: npm warned about removing duplicated packages from `dependencies` in favor of `devDependencies`. - Root cause: same frontend build packages declared in both sections. - Exact fix: npm normalized entries during install (kept in `devDependencies`). - Preventive rule: avoid duplicating build-only packages across both dependency sections. 8. **Build warning persisted (non-blocking)** - Symptom: `[baseline-browser-mapping] ... over two months old` and generated empty chunks in Vite output. - Impact: build still successful. - Action: log-only. 9. **Filament v5 codemod scope gap caused delayed runtime failures** - Symptom: project appeared upgraded, but frontend routes later crashed with missing Filament classes in `cms/*` and `commerce/*`. - Root cause: initial codemod pass was executed for `app/` and `modules/` only, leaving other custom source roots untouched. - Exact fix: run a second migration pass over all custom roots and then verify class existence across repository imports. - Preventive rule: always enumerate all custom source roots before running Filament codemods (`app`, `modules`, `cms`, `commerce`, `support`, etc.). 10. **`vendor/bin/filament-v5` can print Rector cache deletion errors but still exit successfully** - Symptom: commands like `vendor/bin/filament-v5 cms` and `vendor/bin/filament-v5 commerce` reported `Unable to delete ... rector_cached_files ... No such file or directory`, yet finished with success messaging. - Impact: misleading signal that migration completed for target directory. - Exact fix: do not trust command success text alone; run a post-codemod class-import existence scan and targeted runtime smoke checks. - Preventive rule: treat codemod output warnings as potential partial-failure even with zero exit code. 11. **Filament v5 namespace migration gaps (schemas vs forms)** - Symptom: `Class "Filament\\Forms\\Components\\Fieldset" not found` on runtime page render. - Root cause: layout/util classes moved to `Filament\\Schemas\\...` but legacy imports remained in `cms/Filament/Form/*`. - Exact fix: migrate imports: - `Filament\\Forms\\Components\\Fieldset` -> `Filament\\Schemas\\Components\\Fieldset` - `Filament\\Forms\\Components\\Grid` -> `Filament\\Schemas\\Components\\Grid` - `Filament\\Forms\\Components\\Component` -> `Filament\\Schemas\\Components\\Component` - `Filament\\Forms\\Get|Set` -> `Filament\\Schemas\\Components\\Utilities\\Get|Set` - `Filament\\Forms\\Components\\Tabs\\Tab` -> `Filament\\Schemas\\Components\\Tabs\\Tab` - `Filament\\Forms\\Components\\Section` -> `Filament\\Schemas\\Components\\Section` - Preventive rule: after each major Filament upgrade, scan for old `Filament\\Forms\\Components\\(Fieldset|Grid|Section|Tabs\\Tab)` and `Filament\\Forms\\Get|Set`. 12. **Filament page actions namespace changed** - Symptom: commerce resource page classes still imported `Filament\\Pages\\Actions`. - Root cause: missed codemod transformations in specific folders. - Exact fix: replace with `use Filament\\Actions;` and keep `Actions\\*Action::make()` calls. - Preventive rule: grep for `use Filament\\Pages\\Actions;` after v5 migration and replace all matches. 13. **Resource/relation manager form signature drift** - Symptom: files still used `Filament\\Forms\\Form` type and `form(Form $form): Form` signatures. - Root cause: partial codemod application in commerce resources. - Exact fix: update to Filament v5 schema signature: - Resources: `public static function form(Schema $schema): Schema` - Relation managers: `public function form(Schema $schema): Schema` - Preventive rule: grep for `use Filament\\Forms\\Form;` and remove all remaining occurrences. 14. **`TextInput\\Mask` builder class no longer available in current Filament line** - Symptom: legacy closures typed as `Forms\\Components\\TextInput\\Mask` became invalid and future runtime failures were likely. - Root cause: legacy mask builder API from older Filament line. - Exact fix: replaced with v5-safe numeric/step/prefix configuration instead of removed mask-builder typehints. - Preventive rule: grep for `TextInput\\Mask` and rewrite masks using current v5 text input API. 15. **Running the official Filament upgrade utility can generate a large public-asset diff** - Symptom: `php artisan filament:upgrade --no-interaction` republished many files under `public/js/filament/*`, `public/css/filament/*`, and `public/fonts/filament/*`, then cleared config/route/view caches. - Impact: noisy git diff and potential overwrite risk for locally customized published Filament assets. - Exact fix: run this command intentionally once per upgrade pass, then review/commit asset churn separately and rerun verification (`pint`, tests, build). - Preventive rule: announce this side effect before execution and avoid running it repeatedly without need. 16. **`route:list` can throw due controller-constructor abort guards** - Symptom: `php artisan route:list --path=admin` failed with `Symfony\\Component\\HttpKernel\\Exception\\NotFoundHttpException` originating from `App\\Http\\Controllers\\Public\\VacancyController::__construct()` line 16. - Root cause: constructor contains `abort_unless(..., 404)` and the route-list command instantiates controllers during route metadata collection. - Exact fix: use `php artisan about` + tests/build for upgrade smoke checks when this occurs. Long-term refactor: move hard feature gates out of constructors into middleware/action-level guards. - Preventive rule: avoid hard `abort_*` calls in controller constructors. 17. **npm dependency graph got stuck during ESLint major migration** - Symptom: repeated `npm install` attempts failed with `ERESOLVE could not resolve` while moving `eslint-plugin-vue` to `^10` and `vue-eslint-parser` to `^10`. - Root cause: stale lock/tree state from earlier duplicated dependency declarations caused resolver deadlock. - Exact fix: regenerate the npm install tree cleanly (fresh `node_modules` and lock regeneration), then install the target dependency set. - Preventive rule: when npm repeatedly fails with the same peer conflict after package.json corrections, do a clean dependency graph rebuild instead of retry loops. 18. **`eslint-plugin-vue` v10 required ESLint flat config migration** - Symptom: linting failed with legacy config errors (`plugin:vue/vue3-recommended` not found / invalid config shape with `default` key). - Root cause: newer Vue ESLint plugin line expects modern flat-config flow; legacy `.eslintrc` extends become incompatible. - Exact fix: add `eslint.config.cjs` and migrate rules/plugins there; keep eslint on v9. - Preventive rule: treat ESLint plugin major upgrades as config-schema migrations, not package-only updates. 19. **Tailwind v4 upgrader refuses dirty git state by default** - Symptom: `npx @tailwindcss/upgrade` aborted with “Git directory is not clean”. - Exact fix: run with `--force` on controlled upgrade branches. 20. **Tailwind upgrader scanned backup directories and failed config linking** - Symptom: upgrader reported it could not determine config for CSS files in `node_modules.bak` and `resources/css/app.css`. - Root cause: backup dependency folder was inside repository tree and stylesheet had no explicit config link. - Exact fix: move backup folder outside repo and run upgrader with `-c tailwind.config.js`. 21. **Tailwind upgrader failed to load custom config due legacy helper signature** - Symptom: `Cannot destructure property 'negative' of 'undefined'`. - Root cause: custom `inset: (theme, { negative }) => ...` pattern from older Tailwind config API. - Exact fix: replace with explicit negative-spacing generation helper that only depends on `theme('spacing')`. 22. **Tailwind template migration failed on `@apply` of custom class** - Symptom: `Cannot apply unknown utility class 'pl-container'`. - Root cause: `@apply` referenced custom class aliases instead of raw utilities in `resources/css/utility.css`. - Exact fix: expand `.container` rule to raw utility set (`pl-8 lg:pl-32 pr-8 lg:pr-32`) and rerun upgrader. 23. **Tailwind v4 PostCSS plugin package split** - Symptom: Vite build failed with “trying to use `tailwindcss` directly as a PostCSS plugin”. - Root cause: Tailwind v4 moved PostCSS integration to `@tailwindcss/postcss`. - Exact fix: install `@tailwindcss/postcss` and update `postcss.config.js` plugin key. 24. **`eslint-plugin-tailwindcss` is not Tailwind v4 compatible** - Symptom: linting failed with `ERR_PACKAGE_PATH_NOT_EXPORTED` for `tailwindcss/resolveConfig`. - Root cause: current plugin line peers against Tailwind `^3.4.0` only. - Exact fix: remove `eslint-plugin-tailwindcss` integration from active lint config during Tailwind v4 migration. 25. **Clean npm install exposed missing direct dependency (`lodash`)** - Symptom: Vite build failed resolving `lodash` imported by `resources/js/utilities.js`. - Root cause: package was previously present transitively in old lock state but not declared directly. - Exact fix: add `lodash` as direct dependency. - Preventive rule: after lock regeneration, treat unresolved imports as missing direct dependency declarations and fix explicitly. 26. **Filament schema traversal can access uninitialized component container** - Symptom: runtime crash `Typed property Filament\\Schemas\\Components\\Component::$container must not be accessed before initialization` while resolving block schema metadata. - Root cause: direct child-component traversal (`getChildComponents()`/`getName()`) can touch component internals before Livewire/Filament container initialization. - Exact fix: in `cms/Filament/Blocks/Block.php`, resolve protected `childComponents` via reflection, treat closure child schemas as empty in non-Livewire context, and guard `getName()` with `Throwable` fallback to recursive traversal. - Preventive rule: when extracting schema metadata outside a mounted form lifecycle, avoid direct `getChildComponents()` assumptions and handle closure-based schema definitions safely. ## Command Transcript (Key Commands) 1. `COMPOSER_CACHE_DIR=/tmp/composer-cache composer outdated --direct --format=json` 2. `composer why-not laravel/framework ^12 -t` 3. Multiple Composer major updates (Laravel/Filament ecosystem, plugin replacements, package removals) 4. `npm outdated --json` 5. `npm update` 6. `npm install @inertiajs/vue3@^2 @vitejs/plugin-vue@^6 laravel-vite-plugin@^2 vite@^7 @vueuse/core@^14 swiper@^12 vue-i18n@^11 pinia@^3 imagetools-core@^9 vite-imagetools@^9 @formkit/auto-animate@^0.9 @gtm-support/vue-gtm@^3` 7. `npm install uuid@^13` 8. `npm install qs-esm` 9. `php artisan test --compact` 10. `vendor/bin/pint --dirty` 11. `npm run build` 12. `vendor/bin/filament-v5 cms` 13. `vendor/bin/filament-v5 commerce` 14. `vendor/bin/filament-v5 support` 15. Repository-wide Filament import existence scan against Composer autoload 16. `php artisan tinker --execute="Cms\\Filament\\Form\\Fieldset\\ImageForBlock::make('image'); echo 'ok';"` 17. `php artisan filament:upgrade --no-interaction` 18. `vendor/bin/pint --dirty` 19. `php artisan test --compact` 20. `npm run build` 21. `composer outdated --direct --format=json` 22. `npm outdated --json` 23. `php artisan route:list --path=admin` (fails due constructor abort; see exception #16) 24. `php artisan about` 25. `npm pkg delete dependencies.@typescript-eslint/eslint-plugin dependencies.eslint-plugin-unused-imports dependencies.eslint-plugin-vue` 26. npm dependency graph regeneration for ESLint package upgrades 27. Added flat ESLint config (`eslint.config.cjs`) and migrated rules from `.eslintrc.json` 28. `npx @tailwindcss/upgrade --force -c tailwind.config.js` 29. `npm install -D @tailwindcss/postcss` 30. `npm install lodash` 31. Verification reruns: `npx eslint ...`, `npm run build`, `php artisan test --compact`, `composer outdated --direct --format=json`, `npm outdated --json` 32. `php artisan test tests/Feature/Cms/Blocks/BlockSchemaTest.php --compact` 33. Added regression coverage in `tests/Feature/Cms/Blocks/BlockSchemaTest.php` ## npm Final State ### Upgraded majors completed in this pass - `@inertiajs/vue3` -> `^2.3.13` - `@vitejs/plugin-vue` -> `^6.0.4` - `laravel-vite-plugin` -> `^2.1.0` - `vite` -> `^7.3.1` - `@vueuse/core` -> `^14.2.1` - `pinia` -> `^3.0.4` - `swiper` -> `^12.1.0` - `vue-i18n` -> `^11.2.8` - `imagetools-core` -> `^9.1.0` - `vite-imagetools` -> `^9.0.2` - `@formkit/auto-animate` -> `^0.9.0` - `@gtm-support/vue-gtm` -> `^3.1.0` - `uuid` -> `^13.0.0` - `qs-esm` added for Ziggy v2 compatibility - `tailwindcss` -> `^4.1.18` - `@tailwindcss/postcss` added for Tailwind v4 PostCSS integration - `@typescript-eslint/eslint-plugin` -> `^8.55.0` - `@typescript-eslint/parser` -> `^8.55.0` - `eslint-plugin-unused-imports` -> `^4.4.1` - `eslint-plugin-vue` -> `^10.7.0` - ESLint migrated to flat config with `eslint` `^9.39.2` - `lodash` added as explicit direct dependency ### Remaining npm outdated entries - `eslint` `9.39.2 -> 10.0.0` (left intentionally due current `@typescript-eslint` v8 peer range support targeting eslint 8/9) ## Post-Upgrade Verification - `composer outdated --direct --format=json`: no direct outdated packages (`"installed": []`). - `php artisan test --compact`: `16 passed (28 assertions)` (includes new block schema regression coverage). - `npm run build`: successful client + SSR build on Vite 7. - `vendor/bin/pint --dirty`: passes after fixups. - `php artisan filament:upgrade --no-interaction`: succeeds and republishes Filament assets/caches. - `php artisan route:list --path=admin`: fails because a constructor-level abort is triggered (documented in exception #16). - `npx eslint resources/js/app.js resources/js/ssr.js`: passes with flat ESLint config. ## Historical Lessons (From Earlier Upgrade Logs) - Source consulted: `UPGRADE-LOG-CLEANSHOPPING.md`. - Stale `bootstrap/cache/packages.php` and `bootstrap/cache/services.php` can retain removed service providers after package changes. - Filament major upgrades can require running upgrade tooling for custom roots (`app/` and `modules/`). - Filament upgrade scripts may miss manual fixes; always run targeted grep and tests after codemods. - Tailwind major migrations can fail with advanced config/plugin patterns; run as a dedicated pass. - Filament codemod success output is not sufficient validation: cross-check every custom code root and run class-import existence scans. ## Reusable Rules - Keep this file as the canonical `UPDATE_LOG` for each future upgrade pass. - For each exception, always record: - Symptom - Failing command - Root cause - Exact fix - Preventive rule ## Pending TODOs 1. **Migrate remaining PHPUnit-style tests to idiomatic Pest tests** - Reference guide: `https://pestphp.com/docs/migrating-from-phpunit-guide` - Scope: convert class-based PHPUnit style tests (`tests/Feature/*`, `tests/Unit/*`) to Pest `it()` style where practical. - Keep/validate safety guard before every migration test run: `tests/TestCase.php` must enforce in-memory sqlite ( `database.default=sqlite` and `database.connections.sqlite.database=:memory:`) to prevent accidental writes to non-test databases. ## Session Notes — 2026-02-10 (Browser Testing Bootstrap) 1. **Composer dev-package install can rerun Filament upgrade hooks** - Symptom: installing browser-test dependencies triggered `php artisan filament:upgrade` via Composer scripts, republishing Filament assets and clearing caches. - Failing command: `composer require pestphp/pest-plugin-browser --dev --no-interaction`. - Root cause: project-level `post-update-cmd` in `composer.json` always runs `@php artisan filament:upgrade`. - Exact fix: treat as expected side effect and avoid repeated Composer writes when not needed. - Preventive rule: on upgrade branches, assume Composer package changes may create large Filament asset diffs. 2. **Pest browser tests need Playwright runtime setup in addition to npm package install** - Symptom: browser tests can fail on clean machines if Playwright browser binaries are not installed. - Failing command: `./vendor/bin/pest tests/Browser/...` before Playwright install. - Root cause: installing `playwright` npm package does not guarantee browser binaries are present. - Exact fix: run `npx playwright install` (or `npx playwright install chromium`) after dependency install. - Preventive rule: include Playwright browser install in local + CI bootstrap for browser test suites. 3. **Pest v4 browser tests conflict with `public setUp()` in custom TestCase** - Symptom: fatal error `Access level to Pest\Concerns\Testable::setUp() must be public (as in class Tests\TestCase)`. - Failing command: `./vendor/bin/pest tests/Browser/PageBrowserTest.php`. - Root cause: `tests/TestCase.php` used `public function setUp()`, which conflicts with Pest's trait method visibility expectations. - Exact fix: change `tests/TestCase.php` `setUp()` visibility to `protected`. - Preventive rule: keep custom `setUp()` visibility aligned with Laravel/PHPUnit defaults (`protected`) for Pest compatibility. 4. **Browser title assertions can be empty on Inertia pages during early render** - Symptom: browser test failed with `Expected page title ... but found ''` on a valid Inertia route. - Failing command: `./vendor/bin/pest tests/Browser/PageBrowserTest.php`. - Root cause: in this stack, `