Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions .changelog/bundled-consumer-skill.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
category: added
type: minor
---

Bundle the fast-forward-enum consumer skill with executable examples, optional installation guidance and Composer discovery metadata.
8 changes: 8 additions & 0 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -161,3 +161,11 @@ When changing public API:
- update tests and README in the same pass

When opening repo-maintenance issues against `dev-tools`, prefer separate issues per bug instead of mixing unrelated failures into one report.

## Bundled consumer skill

Read [the consumer skill](resources/skills/fast-forward-enum/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.
9 changes: 9 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -316,3 +316,12 @@ Issues, pull requests, and documentation improvements are welcome.
- [Tests workflow](https://github.com/php-fast-forward/enum/actions/workflows/tests.yml)
- [MIT License](LICENSE)
- [RFC 2119](https://datatracker.ietf.org/doc/html/rfc2119)

## Consumer agent skill

The package includes [fast-forward-enum](resources/skills/fast-forward-enum/SKILL.md) for application integration.
It supports `fast-forward/enum:^0.2`; inspect the installed version.
See [skill installation and lifecycle](resources/skills/fast-forward-enum/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.
6 changes: 6 additions & 0 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,9 @@
"require-dev": {
"fast-forward/dev-tools": "dev-main"
},
"suggest": {
"llm/skills": "Optionally synchronize the bundled consumer skill in your project."
},
"minimum-stability": "stable",
"autoload": {
"psr-4": {
Expand Down Expand Up @@ -73,6 +76,9 @@
},
"grumphp": {
"config-default-path": "vendor/fast-forward/dev-tools/grumphp.yml"
},
"skills": {
"source": "resources/skills"
}
},
"scripts": {
Expand Down
21 changes: 21 additions & 0 deletions resources/skills/fast-forward-enum/LICENSE
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
MIT License

Copyright (c) 2026 Felipe Sayão Lobato Abreu <github@mentordosnerds.com>

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.
77 changes: 77 additions & 0 deletions resources/skills/fast-forward-enum/SKILL.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
---
name: fast-forward-enum
description: Integrate Fast Forward enum traits, catalogs, name/value lookups and transitions with explicit handling of invalid external values.
license: MIT; see LICENSE
compatibility: Supports fast-forward/enum ^0.2 and PHP ^8.3. Requires Composer and declared extensions; inspect the installed version. Earlier releases are outside this contract. Requires ext-mbstring.
metadata:
author: Fast Forward
api-baseline: fast-forward/enum ^0.2
---

# Use enum helpers and catalogs

Use this skill for an application consuming `fast-forward/enum:^0.2`.
Read its instructions, manifest/lockfile and installed source before editing.
Run `composer show fast-forward/enum`. 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. Enable ext-mbstring.
With authority to install the dependency:

```sh
composer require 'fast-forward/enum:^0.2'
```

Create `app.php` in the application root alongside composer.json/vendor:

```php
<?php

declare(strict_types=1);

require __DIR__ . '/vendor/autoload.php';

use FastForward\Enum\Calendar\Weekday;
use FastForward\Enum\Helper\EnumHelper;

$values = EnumHelper::values(Weekday::class);
if ($values !== [1, 2, 3, 4, 5, 6, 7]
|| EnumHelper::fromName(Weekday::class, 'Monday') !== Weekday::Monday
|| EnumHelper::tryFromName(Weekday::class, 'not-a-day') !== null
|| Weekday::tryFrom(0) !== null) {
throw new RuntimeException('Enum lookup contract differs.');
}

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

Requires PHP 8.3 and ext-mbstring; no container or configuration file is needed. Composer package `fast-forward/enum` exposes `FastForward\Enum`. Use a catalog only when its values match the application's domain; otherwise define your own enum in a same-named file.

For a custom string/int backed enum use the HasValues, HasNames, HasNameLookup, HasNameMap, HasValueMap and HasOptions traits from `FastForward\Enum\Trait` as needed. HasOptions maps case names to backing values; it is not an automatically translated UI label map. HasLabel's default label contains the enum class and case name; override label() and implement LabeledEnumInterface for deliberate presentation text. EnumHelper::labels requires that interface. Unit enums have names/cases but no backed values; passing a non-enum or a unit enum to values() raises ValueError.

External strings are case-sensitive for name lookup. Use tryFromName or backed tryFrom for untrusted values, and explicitly distinguish null from a valid zero/empty backing value. The throwing alternatives signal ValueError.

For enum state workflows, implement HasTransitions::transitionMap() with case-name keys and target-case lists in the application's enum file; canTransitionTo checks membership and assertCanTransitionTo raises InvalidTransitionException. It validates a transition, but does not persist or mutate an entity. This skill supports ^0.2; the ^0.1 line is outside this skill's version contract.

## 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-enum/examples/integration.php ./vendor/autoload.php
```

Expected final marker: `fast-forward-enum: 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).
23 changes: 23 additions & 0 deletions resources/skills/fast-forward-enum/examples/integration.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
<?php

declare(strict_types=1);

require $argv[1] ?? dirname(__DIR__, 4) . '/vendor/autoload.php';

use FastForward\Enum\Calendar\Weekday;
use FastForward\Enum\Helper\EnumHelper;

$values = EnumHelper::values(Weekday::class);
if ($values !== [1, 2, 3, 4, 5, 6, 7]
|| EnumHelper::fromName(Weekday::class, 'Monday') !== Weekday::Monday
|| EnumHelper::tryFromName(Weekday::class, 'not-a-day') !== null
|| Weekday::tryFrom(0) !== null) {
throw new RuntimeException('Enum lookup contract differs.');
}
try {
EnumHelper::fromName(Weekday::class, 'not-a-day');
throw new RuntimeException('An invalid enum name unexpectedly resolved.');
} catch (ValueError) {
}

echo "fast-forward-enum: ok\n";
167 changes: 167 additions & 0 deletions resources/skills/fast-forward-enum/references/installation.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,167 @@
# Optional installation of fast-forward-enum

`fast-forward/enum:^0.2` includes this complete package under
`resources/skills/fast-forward-enum`. Run `composer show fast-forward/enum`
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/enum"],
"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/enum --dry-run
composer skills:update fast-forward/enum
```

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/enum/resources/skills/fast-forward-enum"
skill_target=".agents/skills/fast-forward-enum"
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-enum` for Codex or the configured
`$HOME/.claude/skills/fast-forward-enum` 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-enum-$(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/enum/resources/skills/fast-forward-enum --skill fast-forward-enum --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/enum/tree/$SKILL_REF/resources/skills/fast-forward-enum" --skill fast-forward-enum --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-enum --agent codex --yes
# Only for a selected global copy:
npx --yes skills@1.7.2 remove fast-forward-enum --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.
47 changes: 47 additions & 0 deletions tests/ConsumerSkillTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
<?php

declare(strict_types=1);

/**
* Ergonomic utilities for PHP enums, including names, values, lookups, and option maps.
*
* This file is part of fast-forward/enum project.
*
* @author Felipe Sayão Lobato Abreu <github@mentordosnerds.com>
* @license https://opensource.org/licenses/MIT MIT License
*
* @see https://github.com/php-fast-forward/enum
* @see https://github.com/php-fast-forward/enum/issues
* @see https://php-fast-forward.github.io/enum/
* @see https://datatracker.ietf.org/doc/html/rfc2119
*/

namespace FastForward\Enum\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-enum/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-enum: ok', $process->getOutput());
}
}
Loading