From bdb97a6206bbf24de8f13072498df0b6343dc0b3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Felipe=20Say=C3=A3o=20Lobato=20Abreu?= Date: Sat, 24 Oct 2026 09:03:24 -0300 Subject: [PATCH 1/2] chore: standardize skill sources and PHPDoc bootstrap --- .changelog/consistent-skill-sources.md | 11 ++++++++ .gitattributes | 1 + .gitignore | 1 + .php-cs-fixer.dist.php | 38 ++++++++++++++++++++++++++ composer.json | 4 +-- skills.json | 13 +++++---- tests/Factory/CallableFactoryTest.php | 25 +++++++++++++++++ 7 files changed, 85 insertions(+), 8 deletions(-) create mode 100644 .changelog/consistent-skill-sources.md create mode 100644 .php-cs-fixer.dist.php diff --git a/.changelog/consistent-skill-sources.md b/.changelog/consistent-skill-sources.md new file mode 100644 index 0000000..973c40e --- /dev/null +++ b/.changelog/consistent-skill-sources.md @@ -0,0 +1,11 @@ +--- +category: changed +type: patch +--- + +Synchronize the bundled local skill automatically and trust Fast Forward Composer +skill donors through `fast-forward/*`. +Resolve Changelog contributor skills from stable `0.4.x` releases without a +redundant skill allowlist. +Load the project autoloader for PHPDoc checks when DevTools is installed as a +dependency. diff --git a/.gitattributes b/.gitattributes index 369d678..76b3854 100755 --- a/.gitattributes +++ b/.gitattributes @@ -10,3 +10,4 @@ /docs/ export-ignore /README*.md export-ignore /skills.json export-ignore +/.php-cs-fixer.dist.php export-ignore diff --git a/.gitignore b/.gitignore index b017933..0dd8ce3 100755 --- a/.gitignore +++ b/.gitignore @@ -12,3 +12,4 @@ composer.lock /docker_tag /output.log /.agents/ +/.dev-tools/ diff --git a/.php-cs-fixer.dist.php b/.php-cs-fixer.dist.php new file mode 100644 index 0000000..0fa14f1 --- /dev/null +++ b/.php-cs-fixer.dist.php @@ -0,0 +1,38 @@ + + * @license https://opensource.org/licenses/MIT MIT License + * + * @see https://github.com/php-fast-forward/container + * @see https://github.com/php-fast-forward + * @see https://datatracker.ietf.org/doc/html/rfc2119 + */ + +use FastForward\DevTools\Path\WorkingProjectPathResolver; +use PhpCsFixer\Config; +use PhpCsFixer\Finder; + +require __DIR__ . '/vendor/autoload.php'; + +return (new Config()) + ->setRiskyAllowed(false) + ->setFinder(Finder::create()->in(__DIR__)->exclude(WorkingProjectPathResolver::TOOLING_EXCLUDED_DIRECTORIES)) + ->setRules([ + 'phpdoc_indent' => true, + 'phpdoc_order' => ['order' => ['param', 'return', 'throws']], + 'phpdoc_separation' => true, + 'phpdoc_trim' => true, + 'phpdoc_trim_consecutive_blank_line_separation' => true, + 'phpdoc_scalar' => true, + 'phpdoc_types' => true, + 'phpdoc_to_comment' => false, + 'phpdoc_add_missing_param_annotation' => true, + ]); diff --git a/composer.json b/composer.json index 72f1d08..c959265 100644 --- a/composer.json +++ b/composer.json @@ -45,11 +45,11 @@ "allow-plugins": { "ergebnis/composer-normalize": true, "fast-forward/dev-tools": true, + "llm/skills": true, "phpdocumentor/shim": true, "phpro/grumphp": true, "phpro/grumphp-shim": true, - "pyrech/composer-changelogs": true, - "llm/skills": true + "pyrech/composer-changelogs": true }, "platform": { "php": "8.3.0" diff --git a/skills.json b/skills.json index 9ea56bb..47b322e 100644 --- a/skills.json +++ b/skills.json @@ -1,5 +1,5 @@ { - "$schema": "https://raw.githubusercontent.com/roxblnfk/skills/master/resources/skills.schema.json", + "$schema": "https://raw.githubusercontent.com/roxblnfk/skills/1.15.0/resources/skills.schema.json", "target": ".agents/skills", "aliases": [], "discovery": false, @@ -8,19 +8,20 @@ "dependencies": { "composer": { "trusted": [ - "fast-forward/config" + "fast-forward/*" ], "trusted-replace": true } }, "sources": [ + { + "from": "dir", + "path": "resources/skills" + }, { "from": "github", "package": "php-fast-forward/changelog", - "ref": "^0.3.0", - "skills": [ - "fast-forward-changelog" - ] + "ref": "^0.4.0" } ] } diff --git a/tests/Factory/CallableFactoryTest.php b/tests/Factory/CallableFactoryTest.php index fe17868..61b282b 100644 --- a/tests/Factory/CallableFactoryTest.php +++ b/tests/Factory/CallableFactoryTest.php @@ -99,6 +99,11 @@ public function invokeWillThrowRuntimeExceptionIfParameterIsNotAClass(): void $factory($container); } + /** + * Rejects union parameter types that the callable factory cannot resolve. + * + * @return void + */ #[Test] public function unionParameterReceivesInvalidTypeDiagnostic(): void { @@ -108,6 +113,11 @@ public function unionParameterReceivesInvalidTypeDiagnostic(): void $factory($this->prophesize(ContainerInterface::class)->reveal()); } + /** + * Rejects intersection parameter types with the expected diagnostic. + * + * @return void + */ #[Test] public function intersectionParameterReceivesInvalidTypeDiagnostic(): void { @@ -117,6 +127,11 @@ public function intersectionParameterReceivesInvalidTypeDiagnostic(): void $factory($this->prophesize(ContainerInterface::class)->reveal()); } + /** + * Resolves a nullable named dependency from the container. + * + * @return void + */ #[Test] public function nullableNamedParameterStillResolvesDependency(): void { @@ -127,6 +142,11 @@ public function nullableNamedParameterStillResolvesDependency(): void self::assertSame($dependency, $factory($container->reveal())); } + /** + * Rejects a disjunctive normal form parameter type. + * + * @return void + */ #[Test] public function dnfParameterReceivesInvalidTypeDiagnostic(): void { @@ -138,6 +158,11 @@ public function dnfParameterReceivesInvalidTypeDiagnostic(): void $factory($this->prophesize(ContainerInterface::class)->reveal()); } + /** + * Rejects parameters without a named dependency type. + * + * @return void + */ #[Test] public function untypedParameterReceivesInvalidTypeDiagnostic(): void { From 95817c66047efa5f378d5d354c65e4f86d5fbe7c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Felipe=20Say=C3=A3o=20Lobato=20Abreu?= Date: Sun, 25 Oct 2026 00:02:48 -0300 Subject: [PATCH 2/2] fix: satisfy full quality checks without bypasses --- examples/06-factory-helpers.php | 2 -- examples/bootstrap.php | 2 +- src/AggregateContainer.php | 4 +--- tests/Factory/CallableFactoryTest.php | 10 +++++----- 4 files changed, 7 insertions(+), 11 deletions(-) diff --git a/examples/06-factory-helpers.php b/examples/06-factory-helpers.php index f518553..0cf10d5 100644 --- a/examples/06-factory-helpers.php +++ b/examples/06-factory-helpers.php @@ -121,8 +121,6 @@ public function __construct( /** * @param DeploymentStage $stage - * - * @return self */ public static function fromStage(DeploymentStage $stage): self { diff --git a/examples/bootstrap.php b/examples/bootstrap.php index be1f8f2..5d94a94 100644 --- a/examples/bootstrap.php +++ b/examples/bootstrap.php @@ -16,7 +16,7 @@ * @see https://datatracker.ietf.org/doc/html/rfc2119 */ -require dirname(__DIR__) . '/vendor/autoload.php'; +require __DIR__ . '/../vendor/autoload.php'; /** * Prints a standard title for CLI examples. diff --git a/src/AggregateContainer.php b/src/AggregateContainer.php index c77a0f8..dfca77b 100644 --- a/src/AggregateContainer.php +++ b/src/AggregateContainer.php @@ -83,9 +83,7 @@ public function append(PsrContainerInterface $container): void { $this->containers[] = $container; - if (! isset($this->resolved[$container::class])) { - $this->resolved[$container::class] = $container; - } + $this->resolved[$container::class] ??= $container; } /** diff --git a/tests/Factory/CallableFactoryTest.php b/tests/Factory/CallableFactoryTest.php index 61b282b..487cd0b 100644 --- a/tests/Factory/CallableFactoryTest.php +++ b/tests/Factory/CallableFactoryTest.php @@ -99,6 +99,7 @@ public function invokeWillThrowRuntimeExceptionIfParameterIsNotAClass(): void $factory($container); } + /** * Rejects union parameter types that the callable factory cannot resolve. * @@ -107,7 +108,7 @@ public function invokeWillThrowRuntimeExceptionIfParameterIsNotAClass(): void #[Test] public function unionParameterReceivesInvalidTypeDiagnostic(): void { - $factory = new CallableFactory(static fn(ContainerInterface|FactoryInterface $dependency) => $dependency); + $factory = new CallableFactory(static fn(ContainerInterface|FactoryInterface $dependency): ContainerInterface|FactoryInterface => $dependency); $this->expectException(RuntimeException::class); $this->expectExceptionMessage('Parameter "dependency" is not a valid type.'); $factory($this->prophesize(ContainerInterface::class)->reveal()); @@ -121,7 +122,7 @@ public function unionParameterReceivesInvalidTypeDiagnostic(): void #[Test] public function intersectionParameterReceivesInvalidTypeDiagnostic(): void { - $factory = new CallableFactory(static fn(ContainerInterface&FactoryInterface $dependency) => $dependency); + $factory = new CallableFactory(static fn(ContainerInterface&FactoryInterface $dependency): ContainerInterface&FactoryInterface => $dependency); $this->expectException(RuntimeException::class); $this->expectExceptionMessage('Parameter "dependency" is not a valid type.'); $factory($this->prophesize(ContainerInterface::class)->reveal()); @@ -138,7 +139,7 @@ public function nullableNamedParameterStillResolvesDependency(): void $container = $this->prophesize(ContainerInterface::class); $dependency = $this->prophesize(FactoryInterface::class)->reveal(); $container->get(FactoryInterface::class)->willReturn($dependency); - $factory = new CallableFactory(static fn(?FactoryInterface $dependency) => $dependency); + $factory = new CallableFactory(static fn(?FactoryInterface $dependency): ?FactoryInterface => $dependency); self::assertSame($dependency, $factory($container->reveal())); } @@ -151,7 +152,7 @@ public function nullableNamedParameterStillResolvesDependency(): void public function dnfParameterReceivesInvalidTypeDiagnostic(): void { $factory = new CallableFactory( - static fn((ContainerInterface&FactoryInterface)|ServiceProviderInterface $dependency) => $dependency + static fn((ContainerInterface&FactoryInterface)|ServiceProviderInterface $dependency): (ContainerInterface&FactoryInterface)|ServiceProviderInterface => $dependency ); $this->expectException(RuntimeException::class); $this->expectExceptionMessage('Parameter "dependency" is not a valid type.'); @@ -171,5 +172,4 @@ public function untypedParameterReceivesInvalidTypeDiagnostic(): void $this->expectExceptionMessage('Parameter "dependency" is not a valid type.'); $factory($this->prophesize(ContainerInterface::class)->reveal()); } - }