refactor: Übergangs-API der Gesamtsperre entfernt (assertWritable, writable, refusal, writeFeatures, released); DiviLocked verlangt die gesperrten Fähigkeiten, Tests bauen Zustände über ReleaseMatrices
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5.5
parent
c036a92b60
commit
d0d3488df8
@@ -59,15 +59,6 @@ final class CompatibilityMatrix
|
||||
return new self($features);
|
||||
}
|
||||
|
||||
/** @return list<DiviFeature> The features that write (ReleaseGate::assertWritable(), ReleaseStatus::writable()). */
|
||||
public static function writeFeatures(): array
|
||||
{
|
||||
return array_values(array_filter(
|
||||
DiviFeature::cases(),
|
||||
static fn (DiviFeature $feature): bool => $feature->isWrite()
|
||||
));
|
||||
}
|
||||
|
||||
public function of(DiviFeature $feature): FeatureStatus
|
||||
{
|
||||
return $this->features[$feature->value];
|
||||
|
||||
@@ -5,6 +5,7 @@ declare(strict_types=1);
|
||||
namespace LIT\MCP\Integrations\Divi\Release;
|
||||
|
||||
use LIT\MCP\Abilities\ToolRefusal;
|
||||
use LogicException;
|
||||
use RuntimeException;
|
||||
use Throwable;
|
||||
use WP_Error;
|
||||
@@ -13,26 +14,31 @@ defined('ABSPATH') || exit;
|
||||
|
||||
/**
|
||||
* A Divi call refused because a feature it needs is not released (spec §5.5, D6); AbilityRegistrar returns
|
||||
* toError(). Without features it is the general write lock of the overall status (texts of spec §5.6).
|
||||
* toError().
|
||||
*/
|
||||
final class DiviLocked extends RuntimeException implements ToolRefusal
|
||||
{
|
||||
/** @param list<DiviFeature> $features The locked features the refused call needs; [] = writing in general. */
|
||||
public function __construct(public readonly ReleaseStatus $status, public readonly array $features = [])
|
||||
/**
|
||||
* @param non-empty-list<DiviFeature> $features The locked features the refused call needs.
|
||||
* @throws LogicException When $features is empty – a lock always names what it locks.
|
||||
*/
|
||||
public function __construct(public readonly ReleaseStatus $status, public readonly array $features)
|
||||
{
|
||||
if ($features === []) {
|
||||
throw new LogicException('a Divi lock needs the locked features');
|
||||
}
|
||||
|
||||
parent::__construct(sprintf(
|
||||
'%s is %s for LIT MCP – %s refused',
|
||||
$status->versionLabel(),
|
||||
$status->state->value,
|
||||
$features === []
|
||||
? 'write'
|
||||
: implode(', ', array_map(static fn (DiviFeature $feature): string => $feature->value, $features))
|
||||
implode(', ', array_map(static fn (DiviFeature $feature): string => $feature->value, $features))
|
||||
));
|
||||
}
|
||||
|
||||
public function toError(): WP_Error
|
||||
{
|
||||
return $this->features === [] ? $this->status->refusal() : $this->status->refusalFor($this->features);
|
||||
return $this->status->refusalFor($this->features);
|
||||
}
|
||||
|
||||
/** The lock anywhere in $e's chain of previous exceptions (callers wrap failures in WriteFailed). */
|
||||
|
||||
@@ -92,16 +92,6 @@ final class ReleaseGate
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Every writing feature (CompatibilityMatrix::writeFeatures()) – for callers that know no feature.
|
||||
*
|
||||
* @throws DiviLocked
|
||||
*/
|
||||
public static function assertWritable(?int $postId = null): void
|
||||
{
|
||||
self::assertFeatures(CompatibilityMatrix::writeFeatures(), $postId);
|
||||
}
|
||||
|
||||
public static function whileProbing(int $postId, callable $write): mixed
|
||||
{
|
||||
$previous = self::$probe;
|
||||
|
||||
@@ -11,8 +11,8 @@ use WP_Error;
|
||||
defined('ABSPATH') || exit;
|
||||
|
||||
/**
|
||||
* The release of the installed Divi (spec §5.2): the matrix of its features (E1) and, for callers that know no
|
||||
* features, the overall state (the worst feature state) with every problem, and the texts of spec §5.6.
|
||||
* The release of the installed Divi (spec §5.2): the matrix of its features (E1), the overall state (the worst
|
||||
* feature state) with every problem, and the refusal texts of a call whose features are locked (D6).
|
||||
*/
|
||||
final class ReleaseStatus
|
||||
{
|
||||
@@ -20,19 +20,6 @@ final class ReleaseStatus
|
||||
public const SOURCE_DATABASE = 'database';
|
||||
public const SOURCE_AUTO = 'auto';
|
||||
|
||||
private const UNTESTED = 'Achtung: %s ist auf dieser Website noch nicht für LIT MCP freigegeben. Dieses '
|
||||
. 'Werkzeug braucht Divi-Fähigkeiten, die noch nicht geprüft sind; andere Divi-Werkzeuge laufen weiter. '
|
||||
. 'Ein Administrator kann unter Einstellungen → LIT MCP → Divi die Prüfung ausführen und die Version '
|
||||
. 'freigeben. Es wurde nichts geändert.';
|
||||
private const UNTESTED_HINT = 'Den Nutzer bitten, die Divi-Prüfung auszuführen und die Version freizugeben; '
|
||||
. 'bis dahin ein Werkzeug ohne die betroffene Fähigkeit nutzen. lit-site-status zeigt unter components → '
|
||||
. 'divi → features, welche Fähigkeiten und Werkzeuge betroffen sind.';
|
||||
private const INCOMPATIBLE = '%s hat Stellen geändert, die LIT MCP für dieses Werkzeug nutzt; andere '
|
||||
. 'Divi-Werkzeuge laufen weiter. Die betroffenen Werkzeuge bleiben gesperrt, bis LIT MCP aktualisiert '
|
||||
. 'wurde. Es wurde nichts geändert.';
|
||||
private const INCOMPATIBLE_HINT = 'Den Nutzer informieren, dass LIT MCP aktualisiert werden muss; bis dahin '
|
||||
. 'ein Werkzeug ohne die betroffene Fähigkeit nutzen. lit-site-status zeigt unter components → divi → '
|
||||
. 'features, welche Fähigkeiten und Werkzeuge betroffen sind.';
|
||||
private const FEATURE_UNTESTED = 'Achtung: %1$s ist auf dieser Website für %2$s noch nicht für LIT MCP '
|
||||
. 'freigegeben. Das ist gesperrt, bis ein Administrator unter Einstellungen → LIT MCP → Divi die Prüfung '
|
||||
. 'ausführt und die Version freigibt. Es wurde nichts geändert.';
|
||||
@@ -74,43 +61,11 @@ final class ReleaseStatus
|
||||
return new self($matrix->state(), $version, $matrix->source(), $matrix->problems(), $release, $matrix);
|
||||
}
|
||||
|
||||
public static function released(string $version, string $source = self::SOURCE_CODE): self
|
||||
{
|
||||
return new self(ReleaseState::Released, $version, $source);
|
||||
}
|
||||
|
||||
/** Every writing feature is usable – for callers that know no feature (general write lock of DiviLocked). */
|
||||
public function writable(): bool
|
||||
{
|
||||
return $this->matrix->usable(CompatibilityMatrix::writeFeatures());
|
||||
}
|
||||
|
||||
public function versionLabel(): string
|
||||
{
|
||||
return $this->version === '' ? 'Divi (Version unbekannt)' : 'Divi ' . $this->version;
|
||||
}
|
||||
|
||||
/** The error of a refused write. @throws LogicException For a released status. */
|
||||
public function refusal(): WP_Error
|
||||
{
|
||||
return match ($this->state) {
|
||||
ReleaseState::Untested => Errors::make(
|
||||
'divi_untested',
|
||||
sprintf(self::UNTESTED, $this->versionLabel()),
|
||||
self::UNTESTED_HINT,
|
||||
423
|
||||
),
|
||||
ReleaseState::Incompatible => Errors::make(
|
||||
'divi_incompatible',
|
||||
sprintf(self::INCOMPATIBLE, $this->versionLabel()),
|
||||
self::INCOMPATIBLE_HINT,
|
||||
423,
|
||||
['problems' => $this->problems]
|
||||
),
|
||||
ReleaseState::Released => throw new LogicException('a released Divi refuses nothing'),
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* The error of a call refused because some of $features are locked (D6): divi_incompatible when one of them is
|
||||
* incompatible, else divi_untested; HTTP 423. The message names the features in that state, the hint every
|
||||
|
||||
Reference in New Issue
Block a user