From dea88652135334019bfeacd323e77611bb630130 Mon Sep 17 00:00:00 2001 From: corgab Date: Sun, 6 Sep 2026 02:57:10 +0000 Subject: [PATCH 1/4] fix: declare the laravel/framework dependency the package already has src/ imports Illuminate\Foundation (AboutCommand, PendingDispatch, Foundation\Queue\Queueable) and calls the config(), app(), event(), report() and dispatch() helpers, all of which ship only inside laravel/framework: Foundation is not published as a standalone illuminate/* component, so the illuminate/* entries alone did not describe what the package needs. composer.json now requires laravel/framework ^13.0 alongside them, and a manifest test fails if an Illuminate namespace is imported without a package that provides it. Closes #42 --- CLAUDE.md | 1 + composer.json | 1 + tests/Unit/ComposerManifestTest.php | 54 +++++++++++++++++++++++++++++ 3 files changed, 56 insertions(+) create mode 100644 tests/Unit/ComposerManifestTest.php diff --git a/CLAUDE.md b/CLAUDE.md index bdc8121..c1b5a14 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -53,6 +53,7 @@ Quantum (Facade) - **PythonBridge** only passes non-null env vars to preserve boto3 credential chain (IAM Roles). - **QPU safety:** Drivers with `synchronous_safe: false` throw on `->run()` to prevent HTTP timeouts. - **EntropyGenerator::integer()** uses rejection sampling on a 256-bit batch buffer — never modulo. +- **composer.json requires laravel/framework:** the package uses `Illuminate\Foundation` (AboutCommand, PendingDispatch, the `config()`/`app()`/`event()`/`dispatch()` helpers), which is not published as a standalone `illuminate/*` component. `tests/Unit/ComposerManifestTest.php` guards the declaration. ## Config diff --git a/composer.json b/composer.json index a264b43..42ab16e 100644 --- a/composer.json +++ b/composer.json @@ -18,6 +18,7 @@ }, "require": { "php": "^8.3", + "laravel/framework": "^13.0", "illuminate/console": "^13.0", "illuminate/contracts": "^13.0", "illuminate/database": "^13.0", diff --git a/tests/Unit/ComposerManifestTest.php b/tests/Unit/ComposerManifestTest.php new file mode 100644 index 0000000..5ae38c3 --- /dev/null +++ b/tests/Unit/ComposerManifestTest.php @@ -0,0 +1,54 @@ + */ +function illuminateNamespacesUsedInSrc(): array +{ + $namespaces = []; + + foreach (new RecursiveIteratorIterator(new RecursiveDirectoryIterator(__DIR__.'/../../src')) as $file) { + if (! $file->isFile() || $file->getExtension() !== 'php') { + continue; + } + + preg_match_all('/^use Illuminate\\\\([A-Za-z]+)\\\\/m', (string) file_get_contents($file->getPathname()), $matches); + $namespaces = [...$namespaces, ...$matches[1]]; + } + + return array_values(array_unique($namespaces)); +} + +it('requires laravel/framework because the package uses Illuminate\Foundation directly', function () { + $require = composerManifest()['require']; + + expect($require)->toHaveKey('laravel/framework') + ->and($require['laravel/framework'])->toBe('^13.0'); +}); + +it('declares every Illuminate namespace it imports', function () { + $require = composerManifest()['require']; + $namespaces = illuminateNamespacesUsedInSrc(); + + expect($namespaces)->toContain('Foundation'); + + foreach ($namespaces as $namespace) { + $component = 'illuminate/'.strtolower($namespace); + $declared = array_key_exists($component, $require) || array_key_exists('laravel/framework', $require); + + expect($declared)->toBeTrue("Illuminate\\{$namespace} is imported in src/ but neither {$component} nor laravel/framework is required."); + } +}); From 5eac91585e7da0772ad66ef28c5a6e0caa7f3646 Mon Sep 17 00:00:00 2001 From: corgab Date: Sun, 6 Sep 2026 03:03:17 +0000 Subject: [PATCH 2/4] fix: pin the framework itself in the CI matrix With laravel/framework required, the four illuminate/* entries only duplicated what the framework already replaces, and the CI matrix pinned those components instead of the framework that actually gets installed; the matrix now pins laravel/framework per Laravel version. The manifest test asserts the requirement itself, not its version constraint, and checks that src still uses Illuminate\Foundation classes or helpers, which is what justifies it. --- .github/workflows/tests.yml | 2 +- CLAUDE.md | 2 +- composer.json | 4 --- tests/Unit/ComposerManifestTest.php | 53 ++++++++++++----------------- 4 files changed, 23 insertions(+), 38 deletions(-) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 2f04815..83d3e16 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -30,7 +30,7 @@ jobs: - name: Install dependencies run: | - composer require "illuminate/support:${{ matrix.laravel }}" "illuminate/console:${{ matrix.laravel }}" "illuminate/contracts:${{ matrix.laravel }}" "illuminate/database:${{ matrix.laravel }}" --no-interaction --no-update + composer require "laravel/framework:${{ matrix.laravel }}" --no-interaction --no-update composer update --prefer-dist --no-interaction - name: Check code style diff --git a/CLAUDE.md b/CLAUDE.md index c1b5a14..4af36e1 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -53,7 +53,7 @@ Quantum (Facade) - **PythonBridge** only passes non-null env vars to preserve boto3 credential chain (IAM Roles). - **QPU safety:** Drivers with `synchronous_safe: false` throw on `->run()` to prevent HTTP timeouts. - **EntropyGenerator::integer()** uses rejection sampling on a 256-bit batch buffer — never modulo. -- **composer.json requires laravel/framework:** the package uses `Illuminate\Foundation` (AboutCommand, PendingDispatch, the `config()`/`app()`/`event()`/`dispatch()` helpers), which is not published as a standalone `illuminate/*` component. `tests/Unit/ComposerManifestTest.php` guards the declaration. +- **composer.json requires laravel/framework, not illuminate/* components:** the package uses `Illuminate\Foundation` (AboutCommand, PendingDispatch, the `config()`/`app()`/`event()`/`dispatch()` helpers), which only ships inside the framework. The CI matrix pins `laravel/framework` per Laravel version; `tests/Unit/ComposerManifestTest.php` guards the requirement. ## Config diff --git a/composer.json b/composer.json index 42ab16e..9bbdc7a 100644 --- a/composer.json +++ b/composer.json @@ -19,10 +19,6 @@ "require": { "php": "^8.3", "laravel/framework": "^13.0", - "illuminate/console": "^13.0", - "illuminate/contracts": "^13.0", - "illuminate/database": "^13.0", - "illuminate/support": "^13.0", "symfony/process": "^7.0" }, "require-dev": { diff --git a/tests/Unit/ComposerManifestTest.php b/tests/Unit/ComposerManifestTest.php index 5ae38c3..430c2c3 100644 --- a/tests/Unit/ComposerManifestTest.php +++ b/tests/Unit/ComposerManifestTest.php @@ -3,52 +3,41 @@ declare(strict_types=1); /** - * Guards composer.json against undeclared dependencies: every Illuminate - * namespace the package imports must come from a package it requires. - * - * Illuminate\Foundation (AboutCommand, PendingDispatch, Foundation\Queue\Queueable - * and the config()/app()/event()/dispatch() helpers) is not published as a - * standalone illuminate/* package, so using it means requiring laravel/framework. + * Guards composer.json against the undeclared dependency the package used to + * have: Illuminate\Foundation (AboutCommand, PendingDispatch, the queue + * Queueable trait and the config()/app()/event()/dispatch() helpers) ships + * only inside laravel/framework, never as a standalone illuminate/* package. */ -function composerManifest(): array +function composerRequire(): array { - return json_decode((string) file_get_contents(__DIR__.'/../../composer.json'), true, flags: JSON_THROW_ON_ERROR); + $manifest = json_decode((string) file_get_contents(__DIR__.'/../../composer.json'), true, flags: JSON_THROW_ON_ERROR); + + return $manifest['require']; } -/** @return list */ -function illuminateNamespacesUsedInSrc(): array +function srcUsesFoundation(): bool { - $namespaces = []; - foreach (new RecursiveIteratorIterator(new RecursiveDirectoryIterator(__DIR__.'/../../src')) as $file) { if (! $file->isFile() || $file->getExtension() !== 'php') { continue; } - preg_match_all('/^use Illuminate\\\\([A-Za-z]+)\\\\/m', (string) file_get_contents($file->getPathname()), $matches); - $namespaces = [...$namespaces, ...$matches[1]]; + $source = (string) file_get_contents($file->getPathname()); + + if (str_contains($source, 'Illuminate\\Foundation\\') + || preg_match('/\b(config|app|event|dispatch|report|base_path|config_path)\(/', $source) === 1) { + return true; + } } - return array_values(array_unique($namespaces)); + return false; } -it('requires laravel/framework because the package uses Illuminate\Foundation directly', function () { - $require = composerManifest()['require']; - - expect($require)->toHaveKey('laravel/framework') - ->and($require['laravel/framework'])->toBe('^13.0'); +it('requires laravel/framework', function () { + expect(composerRequire())->toHaveKey('laravel/framework'); }); -it('declares every Illuminate namespace it imports', function () { - $require = composerManifest()['require']; - $namespaces = illuminateNamespacesUsedInSrc(); - - expect($namespaces)->toContain('Foundation'); - - foreach ($namespaces as $namespace) { - $component = 'illuminate/'.strtolower($namespace); - $declared = array_key_exists($component, $require) || array_key_exists('laravel/framework', $require); - - expect($declared)->toBeTrue("Illuminate\\{$namespace} is imported in src/ but neither {$component} nor laravel/framework is required."); - } +it('needs the framework because src uses Illuminate\Foundation classes or helpers', function () { + // If this ever turns false the package could move back to illuminate/* components. + expect(srcUsesFoundation())->toBeTrue(); }); From b71f70a98e731d79f7d78d34c83fb9d1c244755c Mon Sep 17 00:00:00 2001 From: corgab Date: Thu, 10 Sep 2026 13:16:18 +0200 Subject: [PATCH 3/4] Apply review feedback: wrap global functions in function_exists --- tests/Unit/ComposerManifestTest.php | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/tests/Unit/ComposerManifestTest.php b/tests/Unit/ComposerManifestTest.php index 430c2c3..553fb4e 100644 --- a/tests/Unit/ComposerManifestTest.php +++ b/tests/Unit/ComposerManifestTest.php @@ -8,6 +8,7 @@ * Queueable trait and the config()/app()/event()/dispatch() helpers) ships * only inside laravel/framework, never as a standalone illuminate/* package. */ +if (! function_exists('composerRequire')) { function composerRequire(): array { $manifest = json_decode((string) file_get_contents(__DIR__.'/../../composer.json'), true, flags: JSON_THROW_ON_ERROR); @@ -15,6 +16,9 @@ function composerRequire(): array return $manifest['require']; } +} + +if (! function_exists('srcUsesFoundation')) { function srcUsesFoundation(): bool { foreach (new RecursiveIteratorIterator(new RecursiveDirectoryIterator(__DIR__.'/../../src')) as $file) { @@ -32,6 +36,7 @@ function srcUsesFoundation(): bool return false; } +} it('requires laravel/framework', function () { expect(composerRequire())->toHaveKey('laravel/framework'); From 394bf88d4f846e9bccc143db19665a2164a4c801 Mon Sep 17 00:00:00 2001 From: corgab Date: Thu, 10 Sep 2026 11:18:37 +0000 Subject: [PATCH 4/4] style: run pint on the review-feedback commit Co-Authored-By: Claude Sonnet 5 Claude-Session: https://claude.ai/code/session_01E8zT5sUCTC8TgcpsME4WP5 --- tests/Unit/ComposerManifestTest.php | 38 ++++++++++++++--------------- 1 file changed, 19 insertions(+), 19 deletions(-) diff --git a/tests/Unit/ComposerManifestTest.php b/tests/Unit/ComposerManifestTest.php index 553fb4e..49a592e 100644 --- a/tests/Unit/ComposerManifestTest.php +++ b/tests/Unit/ComposerManifestTest.php @@ -9,33 +9,33 @@ * only inside laravel/framework, never as a standalone illuminate/* package. */ if (! function_exists('composerRequire')) { -function composerRequire(): array -{ - $manifest = json_decode((string) file_get_contents(__DIR__.'/../../composer.json'), true, flags: JSON_THROW_ON_ERROR); + function composerRequire(): array + { + $manifest = json_decode((string) file_get_contents(__DIR__.'/../../composer.json'), true, flags: JSON_THROW_ON_ERROR); - return $manifest['require']; -} + return $manifest['require']; + } } if (! function_exists('srcUsesFoundation')) { -function srcUsesFoundation(): bool -{ - foreach (new RecursiveIteratorIterator(new RecursiveDirectoryIterator(__DIR__.'/../../src')) as $file) { - if (! $file->isFile() || $file->getExtension() !== 'php') { - continue; + function srcUsesFoundation(): bool + { + foreach (new RecursiveIteratorIterator(new RecursiveDirectoryIterator(__DIR__.'/../../src')) as $file) { + if (! $file->isFile() || $file->getExtension() !== 'php') { + continue; + } + + $source = (string) file_get_contents($file->getPathname()); + + if (str_contains($source, 'Illuminate\\Foundation\\') + || preg_match('/\b(config|app|event|dispatch|report|base_path|config_path)\(/', $source) === 1) { + return true; + } } - $source = (string) file_get_contents($file->getPathname()); - - if (str_contains($source, 'Illuminate\\Foundation\\') - || preg_match('/\b(config|app|event|dispatch|report|base_path|config_path)\(/', $source) === 1) { - return true; - } + return false; } - - return false; -} } it('requires laravel/framework', function () {