- Remove laravel/boost (+ its deps laravel/mcp, laravel/roster) and Boost's
.cursor/ skills + MCP registration. No version sweep (composer remove).
- AGENTS.md is now the hand-maintained source of truth (was Boost-generated):
folds in the CLAUDE.md content + a Conventions section codifying the migration
FK rule (unsignedInteger for INT-PK refs, not foreignId — MySQL error 3780)
and the MySQL/PostgreSQL/SQLite cross-DB requirement.
- CLAUDE.md / GEMINI.md / .github/copilot-instructions.md are now gitignored
symlinks to AGENTS.md, created by bin/ai-docs.php (composer run ai-docs, also
wired into post-autoload-dump).
Ports #639 to 3.x — the original targets the now feature-frozen 2.x line.
Address::country_name now resolves the localized country name for the
current app locale via Symfony\Component\Intl\Countries, falling back to
the stored name on lookup failure. Adds symfony/intl + a unit test.
Co-authored-by: Lukas Selch <selchlukas@icloud.com>
3.x is now the default branch, so Dependabot re-surfaced the symfony/guzzle
advisories (composer.lock was never bumped on v3). Same fix as v2's #674:
- laravel/framework -> 13.15.0 (CVE-2026-48019, CRLF in the email rule)
- symfony/{mime,http-kernel,mailer,routing,yaml,polyfill-intl-idn} -> patched
- guzzlehttp/psr7 -> 2.11.0 (host-confusion + CRLF advisories)
composer audit clean.
* fix(deps): require invoiceshelf/modules ^3.0.2 (adds registerExchangeRateDriver)
DriverRegistryProvider::registerExchangeRateDrivers() calls
Registry::registerExchangeRateDriver(), which only exists in
invoiceshelf/modules >= 3.0.2. The constraint (^3.0) and the committed
lock (3.0.1) allowed/pinned versions without it, so a fresh
`composer install` (CI, Docker, new clones) boots into:
Call to undefined method InvoiceShelf\Modules\Registry::registerExchangeRateDriver()
Pin to ^3.0.2 and update the lock so every install gets a version that
has the method. `php artisan package:discover` verified clean.
* fix(ai): register the AI driver via the generic Registry::registerDriver('ai', ...)
registerAiDriver() is a convenience wrapper that is NOT present in any
published invoiceshelf/modules release (only the generic registerDriver()
and registerExchangeRateDriver() ship), so DriverRegistryProvider crashed
app boot on a clean composer install:
Call to undefined method InvoiceShelf\Modules\Registry::registerAiDriver()
Use registerDriver('ai', 'openrouter', ...) instead -- it stores under the
'ai' type exactly like the wrapper would, and AiConfigurationService /
AiDriverFactory read it back via allDrivers('ai') / driverMeta('ai', ...).
Verified by clean-reinstalling invoiceshelf/modules (no local patch) and
running `php artisan package:discover` -> boots clean.
* style: fix pre-existing Pint violations in backup services
BackupService.php and BackupConfigurationFactory.php (untouched by this
PR's boot fix) carried style violations from an earlier domain-reorg
refactor (6d1816bd). `pint --test` checks the whole tree and runs on any
PR that touches PHP, so these failed CI here. Auto-fixed with Pint
(braces_position, no_unused_imports, single_line_empty_body) so the check
goes green.
* build(deps): pull invoiceshelf/modules ^3.0.3 via VCS, restore registerAiDriver()
The 3.0.3 release adds Registry::registerAiDriver() (the method DriverRegistryProvider
and AiDriverFactoryTest call). Packagist has the package frozen, so resolve it directly
from the canonical GitHub repo via a composer VCS repository and require ^3.0.3 (the tag
exists; the freeze is Packagist-side only).
Now that the method ships, restore DriverRegistryProvider to Registry::registerAiDriver()
— reverting the temporary generic registerDriver('ai', ...) workaround — so it matches the
package's intended API and the existing tests. The provider is now net-identical to 3.x.
Verified: php artisan package:discover boots clean; the AI suite (incl. the previously
failing AiDriverFactoryTest) passes.
* test: provision modules_statuses.json in the test bootstrap
The Modules/HelloWorld integration test needs the module enabled at the nwidart
level, read from storage/app/modules_statuses.json at app boot. That file is
gitignored (created locally by `module:make`), so it's absent on CI and fresh
clones — HelloWorld stays disabled and the 5 integration tests fail with 404s.
Provision it (only if missing) in tests/Pest.php before any test boots the app,
so CI matches a local dev environment. Full suite: 462 pass.
* fix(test): enable HelloWorld via a committed modules_statuses.json
The Modules/HelloWorld integration test needs the module enabled at the nwidart
level — read from storage/app/modules_statuses.json by FileActivator. That file
is gitignored, so it's absent in CI / fresh clones, leaving HelloWorld disabled
and the 5 integration tests failing with 404s.
The previous tests/Pest.php provisioning (60a7f0d6) only worked under
`./vendor/bin/pest`. CI runs `php artisan test`, which boots the console app
first; FileActivator reads and caches the (absent) statuses at construction
BEFORE Pest.php runs, so test-runtime provisioning is too late. The file must
exist before any boot.
Commit the file via a storage/app/.gitignore negation, and revert the
ineffective Pest.php hack. Prod-safe: Modules/ is gitignored and not copied by
release.yaml, so a phantom "HelloWorld: true" status is ignored by nwidart (no
such module on disk). bootstrap/cache/modules.php is gitignored (absent in CI),
so nothing overrides the committed file.
Verified with `php artisan test --filter=HelloWorld` (the CI command) and the
full suite: 462 pass; pint clean.
* fix(test): commit the Modules/HelloWorld sample module
HelloWorldIntegrationTest exercises Modules/HelloWorld end-to-end, but Modules/
was gitignored (/Modules), so the module was absent from the repo and from CI —
the 5 integration tests 404'd, and the committed modules_statuses.json merely
enabled a module that wasn't there.
Track Modules/HelloWorld (the test fixture) via a `/Modules/*` + `!HelloWorld`
negation. Not shipped to prod (release.yaml omits Modules/). Now the module is
present, autoloaded (merge-plugin), and enabled (statuses file), so its provider
boots and the menu/settings/routes register.
* build: drop the boost:update post-update-cmd hook (breaks CI)
laravel/boost gates its commands to the local environment, so `php artisan
boost:update` fails in CI ("There are no commands defined in the boost
namespace"), making composer's post-update-cmd return exit 1.
It only began failing the build once Modules/HelloWorld/composer.json was
committed: the wikimedia merge-plugin then runs composer's update path on a
plain `composer install`, triggering post-update-cmd. Drop the auto-update
hook (run `boost:update` manually when needed); vendor:publish stays.
Pulls invoiceshelf/modules ^3.0 from packagist — a thin extension package on
top of current upstream nwidart/laravel-modules ^13.0 — replacing the stale
2021-era invoiceshelf/modules ^1.0 fork that bundled its own copy of nwidart.
The host app's autoloader now resolves Nwidart\Modules\* from
vendor/nwidart/laravel-modules and InvoiceShelf\Modules\* from
vendor/invoiceshelf/modules. Existing imports of Nwidart\Modules\Facades\Module
keep working unchanged.
config/modules.php is republished from upstream v13 with two
InvoiceShelf-specific overrides:
- activators.file.statuses-file kept at storage/app/modules_statuses.json so
existing installations don't lose track of which modules are enabled when
the config is republished (upstream v13 defaults to base_path()).
- New lang/menu and lang/settings entries in stubs.files / stubs.replacements
that pair with the custom module:make stubs shipped from the package.
Wires wikimedia/composer-merge-plugin (a transitive dependency of nwidart) so
each module's nested composer.json autoload mapping is merged into the host
autoloader at composer dump-autoload time. This is what makes a module
generated via php artisan module:make MyModule actually loadable. The plugin
is added to allow-plugins and configured via extra.merge-plugin.include.
Drops the stale Modules\\: Modules/ PSR-4 fallback root from autoload — it
didn't match nwidart's app/-prefixed module layout and was always broken for
generated modules.
* Upgrade the mail configuration
* Update mail configuration to match Laravel 12
* Update mail configuration to properly set none or null
* Pint code
* Upgrade Symfony Mailers
* Convert string references to `::class`
PHP 5.5.9 adds the new static `class` property which provides the fully qualified class name. This is preferred over using strings for class names since the `class` property references are checked by PHP.
* Use Faker methods
Accessing Faker properties was deprecated in Faker 1.14.
* Convert route options to fluent methods
Laravel 8 adopts the tuple syntax for controller actions. Since the old options array is incompatible with this syntax, Shift converted them to use modern, fluent methods.
* Adopt class based routes
* Remove default `app` files
* Shift core files
* Streamline config files
* Set new `ENV` variables
* Default new `bootstrap/app.php`
* Re-register HTTP middleware
* Consolidate service providers
* Re-register service providers
* Re-register routes
* Re-register scheduled commands
* Bump Composer dependencies
* Use `<env>` tags for configuration
`<env>` tags have a lower precedence than system environment variables making it easier to overwrite PHPUnit configuration values in additional environments, such a CI.
Review this blog post for more details on configuration precedence when testing Laravel: https://jasonmccreary.me/articles/laravel-testing-configuration-precedence/
* Adopt anonymous migrations
* Rename `password_resets` table
* Convert `$casts` property to method
* Adopt Laravel type hints
* Mark base controller as `abstract`
* Remove `CreatesApplication` testing trait
* Shift cleanup
* Fix shift first issues
* Updating Rules for laravel 11, sanctum config and pint
* Fix Carbon issue on dashboard
* Temporary fix for tests while migration is issue fixed on laravel side
* Carbon needs numerical values, not strings
* Minimum php version
* Fix domain installation step not fetching the correct company_id
* Fix Role Policy wasn't properly registered
---------
* Create PHP CS Fixer config and add to CI workflow
* Run php cs fixer on project
* Add newline at end of file
* Update to use PHP CS Fixer v3
* Run v3 config on project
* Run seperate config in CI