feat(compat): Profile, Gates und Pods über den Kompatibilitätskern

Neu: Tier, Profile, Compatibility, CapabilityLocked, CapabilityGate, CompatibilityComponent, ProvidesCompatibility, DiviProfile sowie PodsFeature, PodsCompatibility und PodsIntegration. Das CapabilityGate sperrt gesperrte und unbekannte Fähigkeits-IDs (übernimmt die Sperre unbekannter IDs vom DiviFeatureGate) und delegiert nur Divi ausdrücklich. Pods-Schema, -Lesen, -Schreiben und -Feldrechte laufen über usable(PodsFeature::…); Schreiben verlangt FieldRead und FieldWrite, damit ein Rollback keine Werte verliert. lit-site-status meldet Pods über die CompatibilityComponent.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Jan-Niclas Loosen
2026-11-06 12:26:54 +01:00
co-authored by Claude Opus 5.5
parent b511ef6348
commit c85131603b
38 changed files with 2259 additions and 135 deletions
@@ -6,6 +6,9 @@ namespace LIT\MCP\Tests\Unit\Integrations;
use Brain\Monkey\Functions;
use LIT\MCP\Abilities\Ability;
use LIT\MCP\Compatibility\Profile;
use LIT\MCP\Compatibility\ProvidesCompatibility;
use LIT\MCP\Compatibility\Tier;
use LIT\MCP\Integrations\Bootable;
use LIT\MCP\Integrations\Core\CoreIntegration;
use LIT\MCP\Integrations\Divi\DiviIntegration;
@@ -107,6 +110,34 @@ final class IntegrationRegistryTest extends TestCase
self::assertSame(['divi', 'pods'], $booted);
}
public function testCompatibilitiesOfAllProvidersInRegistryOrderEvenUnavailable(): void
{
$first = new Profile('a', 'A', Tier::ReportOnly, []);
$second = new Profile('b', 'B', Tier::ReportOnly, []);
$provider = static function (string $id, bool $available, array $profiles): Integration {
$integration = Mockery::mock(Integration::class . ', ' . ProvidesCompatibility::class);
$integration->allows('id')->andReturn($id);
$integration->allows('isAvailable')->andReturn($available);
$integration->allows('compatibilities')->andReturn($profiles);
return $integration;
};
$registry = new IntegrationRegistry(
$this->integration('core', true, []),
$provider('x', false, [$first]),
$provider('y', true, [$second])
);
self::assertSame([$first, $second], $registry->compatibilities());
self::assertSame(['a', 'b'], array_map(
static fn (Profile $profile): string => $profile->id,
$registry->compatibility()->profiles()
));
self::assertSame($registry->compatibility(), $registry->compatibility(), 'one shared instance');
self::assertSame([], (new IntegrationRegistry($this->integration('core', true, [])))->compatibilities());
}
private function ability(string $name, bool $direct): Ability
{
$ability = Mockery::mock(Ability::class);