refactor(compat): Freigabe-Bausteine generisch

ReleaseStore, CheckReportStore, CheckReport, CheckStep und CompatibilityCheck liegen im Kern und arbeiten je Profil (Optionen lit_mcp_<id>_releases|_check_report; Divi behält Namen und Schlüssel divi_version). Divi hängt Katalog-, Format- und Schreibtest über Profile::extraSteps an. Ein roter Prüfbericht für den aktuellen Stand sperrt LockOnFailure-Profile bis zur nächsten grünen Prüfung; Sperrgründe nennen nur Schritt-Labels, Ausnahmen gehen ins Error-Log. Profile bekommt testedVersions, stateKey, extraSteps, manifestCheck und reportVersionKey und wird mit benannten Argumenten gebaut.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Jan-Niclas Loosen
2026-11-06 12:51:03 +01:00
co-authored by Claude Opus 5.5
parent c85131603b
commit 7d6a97a54e
48 changed files with 1469 additions and 647 deletions
+78
View File
@@ -0,0 +1,78 @@
<?php
declare(strict_types=1);
namespace LIT\MCP\Compatibility;
use Closure;
use LIT\MCP\Log;
use LIT\MCP\Plugin;
use Throwable;
defined('ABSPATH') || exit;
/**
* The admin check of one profile (spec §6.2) – also wp lit-mcp divi-check: manifest (code and registrations),
* fingerprints (only when a manifest has mirrors), then the extra steps of the profile. A step that throws is a red
* step. Not final so the route and the command can be tested with a mock.
*/
class CompatibilityCheck
{
public function __construct(private Profile $profile, private ManifestCheck $manifests)
{
}
public static function forProfile(Profile $profile, ?ManifestCheck $check = null): self
{
return new self($profile, $check ?? $profile->checker());
}
public function run(int $userId): CheckReport
{
$label = $this->profile->label;
$manifest = self::guarded(fn (): array => ManifestCheck::failed(
array_merge($this->manifests->codeChecks(), $this->manifests->registrationChecks())
));
$steps = [new CheckStep(
CheckStep::MANIFEST,
$manifest,
false,
CheckStep::genericLabel(CheckStep::MANIFEST, $label)
)];
if (array_filter($this->profile->manifests, static fn (Manifest $m): bool => $m->mirrors !== []) !== []) {
$steps[] = new CheckStep(
CheckStep::MIRRORS,
self::guarded(fn (): array => ManifestCheck::failed($this->manifests->mirrorChecks())),
false,
CheckStep::genericLabel(CheckStep::MIRRORS, $label)
);
}
if ($this->profile->extraSteps !== null) {
$steps = [...$steps, ...($this->profile->extraSteps)($manifest === [])];
}
$version = (string) ($this->profile->version === null ? '' : ($this->profile->version)());
return new CheckReport(
wp_generate_uuid4(),
gmdate('c'),
$userId,
$version,
Plugin::VERSION,
$this->profile->stateKey === null ? '' : ($this->profile->stateKey)($version),
$steps
);
}
/** @return list<string> The step's problems; a throwing step is one problem. */
public static function guarded(Closure $step): array
{
try {
return array_values(array_filter((array) $step(), 'is_string'));
} catch (Throwable $e) {
// The report reaches MCP clients through the lock reason; the exception text stays in the log.
Log::failure('compat check step', $e);
return ['Prüfschritt abgebrochen (Details im PHP-Error-Log).'];
}
}
}