Skip to content

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

6 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

rawphp/laravel-capabilities

Status: 0.x pre-stable — not Packagist-published (no stable public API claim).
Install: package VCS or monorepo path — see Install.
Closed design work includes config-wired makeRegistry, durable QueryTableGateway, release-prep metadata. Packagist publish remains a human residual.

Core product capability bus for Laravel.

Define a capability once (schema, authorization, run, approval, audit) and expose it via agent, MCP, HTTP, product CLI, and jobs — same rules, one run().

Doc Where
User guide (this package) docs/user-guide.md
Changelog CHANGELOG.md
Sibling messaging rawphp/laravel-capabilities-messaging
Product CLI rawphp/capabilities-cli
Monorepo design / tutorials laravel-capabilities-monorepo (docs/)

Registry boot: ContainerBindings::makeRegistry is the production factory — it applies config/capabilities.php (surfaces, approval/idempotency drivers, audit, scope, rate limits, etc.) and injects the same store instances the service provider binds for accept/invoke parity.

D-020 helpers (assertSchemaSnapshot, assertParity) are implemented for unit-path DX (durable schema snapshots; multi-surface success/deny class parity with mocks/fakes) — not a live multi-surface HTTP/feature suite. See Testing helpers (D-020).

Install

Not on Packagist yet. Prefer package-repo VCS or a monorepo path checkout.

VCS (package remote)

This tree is published to github.com/rawphp/laravel-capabilities from the monorepo on every push to main.

{
  "repositories": [
    {
      "type": "vcs",
      "url": "https://github.com/rawphp/laravel-capabilities"
    }
  ],
  "require": {
    "rawphp/laravel-capabilities": "dev-main"
  }
}

Path (monorepo contributors)

From a clone of laravel-capabilities-monorepo:

{
  "repositories": [
    {
      "type": "path",
      "url": "../laravel-capabilities-monorepo/packages/laravel-capabilities",
      "options": { "symlink": true }
    }
  ],
  "require": {
    "rawphp/laravel-capabilities": "*@dev"
  }
}
composer update rawphp/laravel-capabilities
php artisan vendor:publish --tag=capabilities-config

Full monorepo install policy, branch-alias, and Packagist checklist: monorepo docs/versioning.md. First-capability walkthrough: monorepo docs/tutorials/first-capability.md.

Peer support / D-011 release gate

This package composes laravel/ai and laravel/mcp as optional peers. Release honesty is matrix + unit contract fixtures, not live SDKs in default package CI.

Matrix location (source of truth)

What Where
Machine-readable matrix src/Adapters/PeerSupportMatrix.php
Config mirror config/capabilities.phppeers.support
Probe defaults PeerVersionProbe uses the matrix (not open-ended *)
Design decision monorepo docs/spec.md D-011

Update PeerSupportMatrix whenever you add/drop a supported peer minor/major after contract fixtures stay green. Bumping the matrix without a green unit contract suite is a release blocker.

Required unit contract suite filters

Default package CI does not install live laravel/ai or laravel/mcp. Contract coverage is mock/fake unit tests only.

Before shipping matrix or adapter changes, run:

# from monorepo root
composer test:core -- --filter=PeerSupportMatrix
composer test:core -- --filter=PeerContract
composer test:core -- --filter=Adapter
composer test:core -- --filter=PeerReleaseGateDocs

# from this package (after composer install of dev deps)
./vendor/bin/pest --filter=PeerSupportMatrix
./vendor/bin/pest --filter=PeerContract
./vendor/bin/pest --filter=Adapter
./vendor/bin/pest --filter=PeerReleaseGateDocs

Minimum intent (aligned with D-011 contract table):

Filter / suite Asserts
PeerSupportMatrix Matrix non-empty; version match/mismatch without live peers
PeerContract Frozen fixtures for AI/MCP tool shapes, probe classes, matrix cells
Adapter AdapterApi, AI/MCP adapters, boot fail/disable paths
PeerReleaseGateDocs This README section stays present (anchor guard)

A matrix or adapter change without these green is not shippable.

Fail / disable boot behaviour

When agent or mcp is enabled and the peer is missing or supportsInstalledPeer() === false:

on_incompatible Behaviour
fail (default) Boot exception — surface does not register
disable Soft-disable surface + CRITICAL log + health disabled_incompatible

Never half-register tools. Partial tool lists on an incompatible peer are refused.

AdapterApi bump rule

AdapterApi versions this package’s bridge shapes (not the peer package version).

  • Bump AdapterApi when Tool/MCP mapping call shapes change (requiresBump is true when previous shape ≠ next shape).
  • Keep AdapterApi::CURRENT and supported() in lockstep with PeerContractFixtures.
  • Apps depend on stable catalog/tool surfaces; they must not hard-code adapter version selection for listing.

Package CI policy (no live peers)

Default package CI:

  • Does not install live laravel/ai or laravel/mcp
  • Uses unit tests with mocks/fakes only
  • Package honesty = PeerSupportMatrix + PeerContractFixtures + adapter unit suite

Live “contract tests against real peer minors” remain aspirational for consumer apps that choose to install peers — not a default CI dependency of this package.

Optional consumer peer-live checklist

Consumer applications that install real peers can run an app-owned peer-live path. This package does not run it for you.

  1. In the app (not this package’s default suite):

    composer require laravel/ai
    composer require laravel/mcp
  2. Pin versions that fall inside the declared matrix cells in PeerSupportMatrix (or override peers.support deliberately and update your own tests).

  3. Run your app test suite (including any invoke/agent/MCP flows you own).

  4. Confirm each installed peer version still matches a supported matrix cell; if not, either pin back or open a package PR that extends the matrix after unit contract fixtures stay green.

  5. Optionally keep a consumer-only CI job that installs peers and exercises app-level smoke tests — never required for package green.

Maintainer release checklist (D-011)

  • PeerSupportMatrix updated for any peer support change
  • Unit contract suite filters above green
  • No half-register paths introduced; fail/disable behaviour unchanged
  • AdapterApi bumped if bridge shapes changed
  • CHANGELOG / release notes list declared peer constraints
  • Default package CI still free of live peer installs

Durable persistence (QueryTableGateway)

Database-backed approval and idempotency stores use a first-party QueryTableGateway (Rawphp\Capabilities\Persistence\QueryTableGateway) implementing TableGateway. The package builds one gateway per table from an Illuminate ConnectionInterface — not a shared Eloquent model layer.

Driver config Key Default Database path
Approval store approval.store database DatabaseApprovalStore + QueryTableGateway on capabilities_approvals
Approval connection approval.connection null (app default) Optional named connection
Idempotency idempotency.driver memory Set databaseDatabaseIdempotencyStore + gateway on capabilities_idempotency
Idempotency connection idempotency.connection null Optional named connection
php artisan vendor:publish --tag=capabilities-config
php artisan vendor:publish --tag=capabilities-migrations
php artisan migrate

Production default path: leave TableGateway unbound; with approval.store / idempotency.driver = database, factories construct QueryTableGateway per table. Missing connection → boot/factory failure (no silent ArrayTableGateway).

Host override (~10 lines) when you need a custom backend or in-memory isolation:

// AppServiceProvider::register()
use Rawphp\Capabilities\Persistence\ArrayTableGateway;
use Rawphp\Capabilities\Persistence\TableGateway;

$this->app->singleton(TableGateway::class, function () {
    return new ArrayTableGateway; // or App\Persistence\CustomTableGateway
});

A host-bound TableGateway is used for both database stores when present. Prefer unbound + dual QueryTableGateway in production. Integrator walkthrough: monorepo first-capability tutorial.

Honesty: this package is still not Packagist-published (path/VCS install only until a human completes the monorepo Packagist checklist). Durable gateway code is unit-tested with connection fakes; default package CI does not require a live MySQL/Postgres.


Testing helpers (D-020)

Consumer app CI should lock every capability’s catalog schema and, where dual-path risk matters, assert multi-surface success/deny class parity. Helpers live on CapabilityRegistry and the Capability facade.

Scope honesty: these exercise registry / adapter unit paths with mocks/fakes. They are not a live multi-surface HTTP/feature suite against real laravel/ai / laravel/mcp peers.

Full first-capability walkthrough: monorepo first-capability tutorial. Design: monorepo docs/spec.md D-020.

assertSchemaSnapshot

Locks input_schema + output_schema from the live catalog against a snapshot. Contract: returns true on match; throws SchemaSnapshotException on drift or missing snapshot file (names the capability and which side mismatched).

use Rawphp\Capabilities\Facades\Capability;

// 1) Durable file path (recommended for app CI):
Capability::assertSchemaSnapshot(
    'create-invoice',
    base_path('tests/fixtures/capability-schemas/create-invoice.schema.json'),
);

// 2) Conventional directory → `{dir}/{name}.schema.json`:
Capability::assertSchemaSnapshot(
    'create-invoice',
    null,
    base_path('tests/fixtures/capability-schemas'),
);

// 3) In-memory envelope (unit convenience):
Capability::assertSchemaSnapshot('create-invoice', [
    'input_schema' => [/* JSON Schema */],
    'output_schema' => [/* JSON Schema */],
]);

Snapshot document shape:

{
  "input_schema": { "type": "object", "properties": { } },
  "output_schema": { "type": "object", "properties": { } }
}

Important: assertSchemaSnapshot('create-invoice') with no path/envelope/directory only resolves the capability and returns true — it does not lock schemas. Always pass a file path, conventional directory, or envelope in CI.

App CI should run snapshots for every capability before release. Update the snapshot file intentionally when the schema change is deliberate.

assertParity

Same valid (or deny-triggering) input → same success/deny class across listed surfaces via the registry choke point (invoke with surface-derived caller). Optional assert callback runs only on successful results.

Capability::assertParity('create-invoice', [
    'input' => [
        'customer_id' => 1,
        'amount_cents' => 2500,
        'currency' => 'USD',
    ],
    'surfaces' => ['http', 'registry', 'ai', 'job'],
    // optional shared invoke options:
    // 'actor' => $user, 'tenant_id' => 't-1', 'scope' => $scope,
    'assert' => function ($result): void {
        // runs only when the surface result is success
        expect($result->data['invoice_id'])->toBeInt();
    },
]);
Option Required Role
surfaces yes (non-empty) Labels to invoke: http, cli, agent, mcp, job, artisan, plus aliases ai → agent, registry → http
input recommended Capability input array (defaults to [])
assert no callable(CapabilityResult): void on success results only
actor / tenant_id / scope / options no Shared invoke context (job surface auto-fills a test job bag when missing)

Important: empty options / missing surfaces throws InvalidArgumentException. There is no assertParity() no-arg form that proves multi-surface parity — you must list surfaces and supply real input for the scenario under test.

Mismatch across surfaces throws ParityAssertionException naming the capability, surfaces, and result classes (success vs deny). Approval-required counts as deny class for parity.

About

No description, website, or topics provided.

Resources

Stars

Watchers

Forks

Releases

Packages

Contributors

Languages