diff --git a/.changelog/bundled-consumer-skill.md b/.changelog/bundled-consumer-skill.md new file mode 100644 index 0000000..4986637 --- /dev/null +++ b/.changelog/bundled-consumer-skill.md @@ -0,0 +1,6 @@ +--- +category: added +type: minor +--- + +Bundle the fast-forward-framework consumer skill with executable examples, optional installation guidance and Composer discovery metadata. diff --git a/.changelog/dependency-freshness.md b/.changelog/dependency-freshness.md new file mode 100644 index 0000000..99c70d0 --- /dev/null +++ b/.changelog/dependency-freshness.md @@ -0,0 +1,6 @@ +--- +category: changed +type: patch +--- + +Raise compatible Fast Forward dependency minimums to current stable releases, use stable DevTools and check Composer and Actions updates daily through Dependabot. diff --git a/.github/dependabot.yml b/.github/dependabot.yml index 21b628d..36a0a4e 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -3,7 +3,20 @@ updates: - package-ecosystem: "composer" directory: "/" schedule: - interval: "weekly" + interval: "daily" + time: "09:00" + timezone: "America/Sao_Paulo" + open-pull-requests-limit: 20 + versioning-strategy: increase + allow: + - dependency-type: "all" + groups: + fast-forward-compatible: + patterns: + - "fast-forward/*" + update-types: + - "minor" + - "patch" commit-message: prefix: "Composer" include: "scope" @@ -14,10 +27,13 @@ updates: - package-ecosystem: "github-actions" directory: "/" schedule: - interval: "weekly" + interval: "daily" + time: "09:00" + timezone: "America/Sao_Paulo" + open-pull-requests-limit: 20 commit-message: prefix: "GitHub Actions" include: "scope" labels: - "github-actions" - - "continuous-integration" \ No newline at end of file + - "continuous-integration" diff --git a/AGENTS.md b/AGENTS.md index bffc133..9a33dfe 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -44,3 +44,11 @@ Fast Forward components through a single provider entry point. - When making user-visible changes, add a unique `.changelog/*.md` fragment using the installed Changelog skill and [contribution guide](CONTRIBUTING.md). - Preserve published `CHANGELOG.md` history. The reviewed version PR consolidates pending fragments; do not add an `Unreleased` heading. - Prefer concise entry wording and include the current PR reference when known. + +## Bundled consumer skill + +Read [the consumer skill](resources/skills/fast-forward-framework/SKILL.md) when changing application integration +examples, consumer guides or the skill. Keep its version contract, references +and executable examples aligned with the supported public API. The canonical +distributable source is `resources/skills`; it needs no installed copy or host +alias. Repository maintenance continues to follow this AGENTS.md. diff --git a/DEPENDENCIES.md b/DEPENDENCIES.md new file mode 100644 index 0000000..ef36d34 --- /dev/null +++ b/DEPENDENCIES.md @@ -0,0 +1,37 @@ +# Dependency freshness + +The framework requires the latest reviewed stable Fast Forward baselines with +caret constraints. A fresh Composer resolution chooses the newest compatible +versions allowed by the entire application graph and its PHP/extensions. + +This library intentionally commits no composer.lock. Applications should commit +their lockfile: composer install reproduces that application lock, while composer +update deliberately resolves new compatible versions. Installing/updating this +library does not rewrite an application's lockfile in the background. + +Dependabot monitors Composer and GitHub Actions daily at 09:00 America/Sao_Paulo +(on Dependabot's weekday daily schedule). Composer uses versioning-strategy: +increase so a new release can propose an increased manifest minimum even when +it was already compatible. All dependency types are eligible; compatible +fast-forward/* minor/patch updates are grouped and major proposals stay separate. +No ignored package/version or cooldown suppresses new Fast Forward releases. +Twenty concurrent version-update PRs are permitted. These settings take effect +when the configuration is merged into the default branch. They prepare reviewable +PRs; checks and maintainer merge decisions still apply. + +HTTP Client, HTTP Factory and HTTP Message are already installed transitively +through fast-forward/http. Requiring them again at framework level is unnecessary +for the framework's own provider. Clock, Config, Container, Defer, Enum, Event +Dispatcher, Fork, HTTP and Iterators form the intended runtime aggregate. +Changelog, DevTools, Composer Installers and the phpDocumentor template are +development/distribution tools rather than extra application runtime services. +Empty/prototype repositories do not establish a supported runtime library. + +For a consumer that needs every newly published compatible release, schedule +reviewed dependency updates in that consumer too. Another dependency or the +consumer's PHP version can constrain resolution; this framework cannot guarantee +that the globally newest release is compatible. Review major releases and +runtime requirement changes explicitly before widening constraints. + +References: [Composer constraints](https://getcomposer.org/doc/articles/versions.md) +and [Dependabot options](https://docs.github.com/en/code-security/reference/supply-chain-security/dependabot-options-reference). diff --git a/README.md b/README.md index f9a5119..48d57f9 100644 --- a/README.md +++ b/README.md @@ -99,3 +99,15 @@ Fast Forward Framework is licensed under the [MIT license](LICENSE). Developed by **Felipe Sayão Lobato Abreu** - 📧 [github@mentordosnerds.com](mailto:github@mentordosnerds.com) + +## Consumer agent skill + +The package includes [fast-forward-framework](resources/skills/fast-forward-framework/SKILL.md) for application integration. +It supports `fast-forward/framework:^1.5`; inspect the installed version. +See [skill installation and lifecycle](resources/skills/fast-forward-framework/references/installation.md) for +optional project synchronization with `llm/skills ^1.15`, guarded manual +project/global copies and a pinned Skills CLI alternative. Shipping files does +not install tooling or activate the skill in an agent session. + +See [dependency freshness](DEPENDENCIES.md) for compatible version ranges, +application lockfiles and the daily Dependabot update policy. diff --git a/composer.json b/composer.json index fa9d795..5180c98 100644 --- a/composer.json +++ b/composer.json @@ -27,18 +27,21 @@ ], "require": { "php": "^8.3", - "fast-forward/clock": "^1.0", - "fast-forward/config": "^1.4", - "fast-forward/container": "^1.5", - "fast-forward/defer": "^1.0", - "fast-forward/enum": "^0.1", + "fast-forward/clock": "^1.1", + "fast-forward/config": "^1.7.3", + "fast-forward/container": "^1.10.1", + "fast-forward/defer": "^1.0.1", + "fast-forward/enum": "^0.1.1", "fast-forward/event-dispatcher": "^1.0", - "fast-forward/fork": "^1.0", - "fast-forward/http": "^1.0", - "fast-forward/iterators": "^1.0" + "fast-forward/fork": "^1.0.1", + "fast-forward/http": "^1.1", + "fast-forward/iterators": "^1.2" }, "require-dev": { - "fast-forward/dev-tools": "dev-main" + "fast-forward/dev-tools": "^1.26" + }, + "suggest": { + "llm/skills": "Optionally synchronize the bundled consumer skill in your project." }, "minimum-stability": "stable", "autoload": { @@ -71,6 +74,9 @@ }, "grumphp": { "config-default-path": "vendor/fast-forward/dev-tools/grumphp.yml" + }, + "skills": { + "source": "resources/skills" } }, "scripts": { diff --git a/resources/skills/fast-forward-framework/LICENSE b/resources/skills/fast-forward-framework/LICENSE new file mode 100644 index 0000000..acddad6 --- /dev/null +++ b/resources/skills/fast-forward-framework/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2026 Felipe Sayão Lobato Abreu + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/resources/skills/fast-forward-framework/SKILL.md b/resources/skills/fast-forward-framework/SKILL.md new file mode 100644 index 0000000..fc96a29 --- /dev/null +++ b/resources/skills/fast-forward-framework/SKILL.md @@ -0,0 +1,88 @@ +--- +name: fast-forward-framework +description: Compose a Fast Forward application container, resolve HTTP, event and clock services, and diagnose missing or conflicting configuration. +license: MIT; see LICENSE +compatibility: Supports fast-forward/framework ^1.5 and PHP ^8.3. Requires Composer and declared extensions; inspect the installed version. Earlier releases are outside this contract. +metadata: + author: Fast Forward + api-baseline: fast-forward/framework ^1.5 +--- + +# Bootstrap the Fast Forward framework + +Use this skill for an application consuming `fast-forward/framework:^1.5`. +Read its instructions, manifest/lockfile and installed source before editing. +Run `composer show fast-forward/framework`. For versions outside this contract, +inspect matching documentation; do not silently upgrade the application. + +## First working integration + +Requires PHP ^8.3, Composer's application autoloader and this package's runtime +dependencies. +With authority to install the dependency: + +```sh +composer require 'fast-forward/framework:^1.5' +``` + +Create `app.php` in the application root alongside composer.json/vendor: + +```php + [FrameworkServiceProvider::class]]); +$container = container($config); +$response = $container->get(ResponseFactoryInterface::class)->createResponse(201); +if ($response->getStatusCode() !== 201 + || !$container->get(ClockInterface::class)->now() instanceof DateTimeImmutable + || !$container->get(EventDispatcherInterface::class) instanceof EventDispatcherInterface) { + throw new RuntimeException('Framework services were not composed.'); +} + +echo "integration ready\n"; +``` + +Run `php app.php`. It throws on a wrong result; success prints +`integration ready`. It uses no production data and starts no server. + +## Wiring and failure boundaries + +The raw provider list is keyed by `FastForward\Container\ContainerInterface::class` in the ArrayConfig passed to `container($config)`. It is exposed as `config.FastForward\Container\ContainerInterface`; the PSR-11 interface is a different key. Do not pass an array to the variadic container helper. The provider composes HTTP, Event Dispatcher and Clock; it does not install an HTTP server, router, cache, logger or middleware pipeline. + +For a provider-only bootstrap, call `container(FrameworkServiceProvider::class)`. Use ArrayConfig when the event integration needs listener configuration. Inject the resulting PSR-11 container into the application's own runtime; obtaining a response does not emit it. HTTP Message, HTTP Factory and HTTP Client arrive transitively through HTTP. + +For application overrides put an explicit ArrayServiceProvider first in the helper's arguments, ahead of the configured/default providers. Aggregate source lookup uses the first successfully resolving source; it does not recursively merge factories across separate sources. Services created by ServiceProviderContainer are cached. A missing id raises a PSR-11 not-found exception; an existing factory can still fail at get(), so has() is not a construction guarantee. + +The helper tries aggregate sources in registration order. A PSR container or +not-found exception from one source permits fallback to another source; other +throwables propagate. Source priority is therefore the first successful resolution, +not a guarantee that an earlier broken factory terminates lookup. Nested provider +list errors during helper composition are caught; if services are absent, inspect +the raw configuration and resolve the provider directly to diagnose the failure. + +## Verify integration + +Run the [complete example](examples/integration.php) against the application's +autoloader in a disposable process. It checks success and negative cases: + +```sh +php /path/to/fast-forward-framework/examples/integration.php ./vendor/autoload.php +``` + +Expected final marker: `fast-forward-framework: ok`. Keep application-specific regression +tests in its existing PHPUnit suite, with each fixture class in its same-named +file. File copying, host discovery and completed agent tasks are different checks. +For optional copying/synchronization load +[installation and lifecycle](references/installation.md). diff --git a/resources/skills/fast-forward-framework/examples/integration.php b/resources/skills/fast-forward-framework/examples/integration.php new file mode 100644 index 0000000..8b8e256 --- /dev/null +++ b/resources/skills/fast-forward-framework/examples/integration.php @@ -0,0 +1,29 @@ + [FrameworkServiceProvider::class]]); +$container = container($config); +$response = $container->get(ResponseFactoryInterface::class)->createResponse(201); +if ($response->getStatusCode() !== 201 + || !$container->get(ClockInterface::class)->now() instanceof DateTimeImmutable + || !$container->get(EventDispatcherInterface::class) instanceof EventDispatcherInterface) { + throw new RuntimeException('Framework services were not composed.'); +} +try { + $container->get('application.missing'); + throw new RuntimeException('Missing service unexpectedly resolved.'); +} catch (\Psr\Container\NotFoundExceptionInterface) { +} + +echo "fast-forward-framework: ok\n"; diff --git a/resources/skills/fast-forward-framework/references/installation.md b/resources/skills/fast-forward-framework/references/installation.md new file mode 100644 index 0000000..854511e --- /dev/null +++ b/resources/skills/fast-forward-framework/references/installation.md @@ -0,0 +1,167 @@ +# Optional installation of fast-forward-framework + +`fast-forward/framework:^1.5` includes this complete package under +`resources/skills/fast-forward-framework`. Run `composer show fast-forward/framework` +and inspect that path before copying. Earlier releases are outside this skill +contract. Composer suggestions do not install anything automatically. Library +installation, discovery-root copies and session activation are separate steps. + +## Team project with llm/skills + +Use Composer 2 and optionally add `llm/skills:^1.15` as development tooling. +Start with a project target for reviewable team configuration. Preserve existing +settings/aliases/approvals when merging these keys into `skills.json` **before** +installing or enabling the plugin: + +```json +{ + "target": ".agents/skills", + "aliases": [], + "discovery": false, + "auto-sync": true, + "vendor-sources": false, + "dependencies": { + "composer": { + "trusted": ["fast-forward/framework"], + "trusted-replace": true + } + } +} +``` + +This replaces implicit dependency trust with a narrow donor allowlist, disables +remote vendor sources and requests no host aliases. A deliberate +`fast-forward/*` allowlist grants broader organizational trust. Use the actual +host's project discovery root; Claude Code can choose `.claude/skills`. +Do not erase intentional aliases or other donors in an existing manifest. + +If the application owner has authorized this plugin's execution, merge its +root approval, install it and inspect the report: + +```sh +composer config allow-plugins.llm/skills true +composer require --dev 'llm/skills:^1.15' +composer skills:show +``` + +The donor only suggests the tool; it neither requires it at runtime nor grants +this approval on behalf of consumers. Commit the chosen manifest, skills.json, +application lockfile and intended project copies according to project policy. +Approved Composer install/update operations then perform automatic sync. + +For manual timing, choose `auto-sync:false` before installation and use: + +```sh +composer skills:show +composer skills:update fast-forward/framework --dry-run +composer skills:update fast-forward/framework +``` + +Explicitly naming a donor selects/trusts it for that run. Test automatic trust +without a package argument when that policy is what matters. With Composer +2.10.3 and llm/skills 1.15.0, `--no-scripts` alone still permits automatic copy; +`auto-sync:false` disables automatic sync and `--no-plugins` stops plugin loading. +Verify these behaviors again after changing tool versions. + +## Update and removal boundaries + +A 1.15 sync overwrites incoming same-path files. It retains extra local files +and files removed from the donor's next version. Removing a donor or uninstalling +the plugin does not delete its existing instruction copies. Regard the copy as +donor-owned; keep deliberate local instructions in a separate skill name. +Record the installed donor/tool versions and review diffs before each refresh. + +Inspect cleanup without applying it: + +```sh +composer skills:update --clean --dry-run +``` + +An unscoped clean can remove all target skills, including handwritten ones. +Scoped clean reinstalls the current selection; it cannot locate an absent donor +by historical ownership. Inventory affected directories, back up outside +discovery, and obtain authority before any destructive cleanup. Do not add a +root-wide clean hook. + +## Guarded manual copying + +Choose the existing project root, then copy all files together: + +```sh +skill_source="vendor/fast-forward/framework/resources/skills/fast-forward-framework" +skill_target=".agents/skills/fast-forward-framework" +test -f "$skill_source/SKILL.md" && +test ! -e "$skill_target" && +test ! -L "$skill_target" && +mkdir -p "$(dirname "$skill_target")" && +cp -R "$skill_source" "$skill_target" +``` + +Both existing directories and dangling links are refused. Global use is an +explicit alternative: set skill_target to +`$HOME/.agents/skills/fast-forward-framework` for Codex or the configured +`$HOME/.claude/skills/fast-forward-framework` for Claude Code. +Do not create a symlink into replaceable vendor content. A globally installed +Composer plugin does not constitute a global instruction copy. + +For a selected manual update/removal, first move only that package into a unique +backup outside discovery: + +```sh +skill_backup="$HOME/.agent-skill-backups/fast-forward-framework-$(date +%Y%m%d%H%M%S)-$$" +test -d "$skill_target" && +test ! -L "$skill_target" && +test ! -e "$skill_backup" && +test ! -L "$skill_backup" && +mkdir -p "$(dirname "$skill_backup")" && +mv "$skill_target" "$skill_backup" +``` + +For an update review the new source, then repeat the guarded copy. To roll back, +archive any replacement and restore the saved whole directory to the same +target. A manager-owned copy may return on its next sync until the consumer +changes that donor's trust/dependency configuration. Keep backups out of every +discovery directory. + +## Optional Skills CLI + +Requires npm, Node.js 22.20.0+ and Git for repository sources. The reviewed pin +is skills@1.7.2; evaluate version changes separately. Choose only the actual host: + +```sh +npx --yes skills@1.7.2 add ./vendor/fast-forward/framework/resources/skills/fast-forward-framework --skill fast-forward-framework --agent codex --copy --yes +npx --yes skills@1.7.2 list --agent codex +``` + +Use `--agent claude-code` for that host. Add `--global` to both commands only +when global scope was selected. Preserve/commit intended project copies and the +CLI's project lockfile. For a remote source first set SKILL_REF to a reviewed +immutable commit: + +```sh +npx --yes skills@1.7.2 add "https://github.com/php-fast-forward/framework/tree/$SKILL_REF/resources/skills/fast-forward-framework" --skill fast-forward-framework --agent codex --copy --yes +``` + +Review the new revision and back up local edits before repeating for an update. +Remove the chosen package/scope with: + +```sh +npx --yes skills@1.7.2 remove fast-forward-framework --agent codex --yes +# Only for a selected global copy: +npx --yes skills@1.7.2 remove fast-forward-framework --agent codex --global --yes +``` + +After every route inspect the complete files, refresh the agent session, +confirm the public name is discovered and perform a bounded consumer task. +A copy, listing and successful task are separate observations. + +Tool contracts: [llm/skills 1.15.0](https://github.com/roxblnfk/skills/tree/1.15.0), +[Composer plugin approval](https://getcomposer.org/doc/06-config.md#allow-plugins) +and [Skills CLI](https://github.com/vercel-labs/skills). + +The Skills CLI 1.7.2 removal report is not proof that the canonical project +`.agents/skills` directory disappeared. A Codex project exercise reported one +removal while that copied package remained. Inspect the actual target and CLI +lockfiles afterwards. If an obsolete copy remains, verify that no other host +uses it, then archive only that selected directory outside discovery using the +manual recovery procedure above. Do not run a blanket clean to compensate. diff --git a/tests/ConsumerSkillTest.php b/tests/ConsumerSkillTest.php new file mode 100644 index 0000000..c7514d3 --- /dev/null +++ b/tests/ConsumerSkillTest.php @@ -0,0 +1,47 @@ + + * @license https://opensource.org/licenses/MIT MIT License + * + * @see https://php-fast-forward.github.io/framework/ + * @see https://github.com/php-fast-forward/framework + * @see https://github.com/php-fast-forward/framework/issues + * @see https://datatracker.ietf.org/doc/html/rfc2119 + */ + +namespace FastForward\Framework\Tests; + +use PHPUnit\Framework\Attributes\CoversNothing; +use PHPUnit\Framework\TestCase; +use Symfony\Component\Process\Process; + +#[CoversNothing] +final class ConsumerSkillTest extends TestCase +{ + /** + * @return void + */ + public function testBundledConsumerIntegration(): void + { + $root = \dirname(__DIR__); + $process = new Process([ + \PHP_BINARY, + $root . '/resources/skills/fast-forward-framework/examples/integration.php', + $root . '/vendor/autoload.php', + ], $root, [ + 'XDEBUG_MODE' => 'off', + ]); + $process->setTimeout(20); + $process->run(); + + self::assertTrue($process->isSuccessful(), $process->getErrorOutput() . $process->getOutput()); + self::assertStringContainsString('fast-forward-framework: ok', $process->getOutput()); + } +}