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
5 changes: 2 additions & 3 deletions includes/admin/class-saddle-rest.php
Original file line number Diff line number Diff line change
Expand Up @@ -934,16 +934,15 @@ public static function clear_agent_memory() {
* future plugin doing the same would land there too — a grouping bug nobody
* would think to look for in a REST controller.
*
* The literals stay as a floor so nothing regroups on sites where a
* contributor enrols later than this runs (Saddle Pro's `knovia-`), and
* The literals stay as a floor: `waggle-` is the catalog default, and
* `unsplash-` is here despite not being a wrapper at all: it is an external
* service from the owner's point of view, which is what this grouping is
* about.
*
* @return string[]
*/
private static function integration_prefixes() {
$prefixes = array( 'waggle-', 'knovia-', 'unsplash-' );
$prefixes = array( 'waggle-', 'unsplash-' );

if ( class_exists( 'Saddle_Integrations' ) ) {
foreach ( array_keys( Saddle_Integrations::integrations() ) as $slug ) {
Expand Down
2 changes: 1 addition & 1 deletion includes/class-saddle-ecosystem.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
* PARKED — Phase 3 ecosystem integration.
*
* This class is intentionally NOT loaded or instantiated anywhere. It is a
* placeholder for cross-product orchestration (inbees/outbees/mailyard/
* placeholder for cross-product orchestration (bridle/outbees/mailyard/
* formyard/flypops), which is explicitly out of scope for v0.1 and v0.2.
*
* Do NOT require or instantiate this without an explicit decision to reopen
Expand Down
10 changes: 5 additions & 5 deletions includes/class-saddle-integration-engine.php
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@
* it IS a `saddle/` ability: `waggle/get-aeo-score` → `saddle/waggle-get-aeo-score`.
*
* One engine, many catalogs: free Saddle instantiates it for the free
* first-party catalog (Waggle), Saddle Pro for its own (Knovia). Each
* first-party catalog (Waggle), Saddle Pro for its own. Each
* caller supplies only its catalog and filter names — the wrap/executor
* safety logic exists exactly once, here.
*
Expand Down Expand Up @@ -257,14 +257,14 @@ public function register_wrappers() {
*
* @param string $slug Integration slug.
* @param array $def Catalog definition (title, force_destructive, …).
* @param string $name Source ability name, e.g. 'knovia/create-doc'.
* @param string $name Source ability name, e.g. 'bridle-kb/create-doc'.
* @param object $ability Source WP_Ability.
* @param array $all The ability registry snapshot from this pass.
*/
private function wrap( $slug, array $def, $name, $ability, array $all ) {
$title = $def['title'];
$source_short = substr( $name, strpos( $name, '/' ) + 1 );
$short = $slug . '-' . $source_short; // knovia-create-doc.
$short = $slug . '-' . $source_short; // bridle-kb-create-doc.
$wrapper = 'saddle/' . $short;

// Collision — never overwrite an existing saddle ability. (Checked
Expand Down Expand Up @@ -388,8 +388,8 @@ private static function canonical( array $args ) {
* (whose own permission_callback core re-checks inside execute()), gate
* destructive calls, and log every mutation.
*
* @param string $short Wrapper short name (knovia-create-doc).
* @param string $name Source ability name (knovia/create-doc).
* @param string $short Wrapper short name (bridle-kb-create-doc).
* @param string $name Source ability name (bridle-kb/create-doc).
* @param string $title Integration title.
* @param bool $destructive Whether the wrapper treats the source as destructive.
* @param bool $is_readonly Whether the source is read-only.
Expand Down
4 changes: 3 additions & 1 deletion includes/class-saddle-integrations.php
Original file line number Diff line number Diff line change
Expand Up @@ -29,10 +29,12 @@ class Saddle_Integrations {
*
* `rank` is Saddle Rank, Waggle renamed in its 2.0 (it enrols itself as
* saddle-rank/*). `waggle` stays for the sites still running Waggle.
* `bridle` and `bridle-kb` are Bridle, the support inbox, and Bridle KB,
* its knowledge base; both enrol themselves (bridle/*, bridle-kb/*).
*
* @var string[]
*/
const FIRST_PARTY = array( 'waggle', 'mailyard', 'analytics', 'rank', 'crm' );
const FIRST_PARTY = array( 'waggle', 'mailyard', 'analytics', 'rank', 'crm', 'bridle', 'bridle-kb' );

/**
* Option holding the third-party slugs the owner has switched on.
Expand Down
1 change: 1 addition & 0 deletions readme.txt
Original file line number Diff line number Diff line change
Expand Up @@ -209,6 +209,7 @@ The WordPress.org version never checks for its own updates. The version from plu
== Changelog ==

= 1.5.2 =
* Changed: Bridle and Bridle KB are PlugPress plugins, so their tools are on as soon as the plugin is active, like Mailyard and SEO. You no longer switch them on under Services.
* Fixed: When a step shows a preview first, such as moving a post to the trash, your AI app now shows you the preview and waits for your yes. Claude Code reads only the start of Saddle's guide, and it could confirm on its own.

= 1.5.1 =
Expand Down
57 changes: 55 additions & 2 deletions tests/integrations-test.php
Original file line number Diff line number Diff line change
Expand Up @@ -619,7 +619,7 @@ public function test_the_prefix_list_picks_up_the_live_catalog() {

$this->assertContains( 'mailyard-', $list, 'A self-enrolled integration must reach the UI grouping.' );
$this->assertContains( 'waggle-', $list );
$this->assertContains( 'knovia-', $list, 'The literal floor keeps Pro’s grouping from regressing.' );
$this->assertNotContains( 'knovia-', $list, 'Saddle Pro no longer wraps a knowledge base; Bridle KB enrols in free Saddle.' );
}

/* -------- third-party integrations: owner-approved -------- */
Expand Down Expand Up @@ -752,7 +752,7 @@ static function () {
* plugin's tools off on every site.
*/
public function test_every_plugpress_plugin_is_first_party() {
foreach ( array( 'waggle', 'mailyard', 'analytics', 'rank', 'crm' ) as $slug ) {
foreach ( array( 'waggle', 'mailyard', 'analytics', 'rank', 'crm', 'bridle', 'bridle-kb' ) as $slug ) {
$this->assertTrue( Saddle_Integrations::is_first_party( $slug ), "{$slug} must be first-party" );
$this->assertContains( $slug, Saddle_Integrations::FIRST_PARTY );
}
Expand Down Expand Up @@ -796,6 +796,59 @@ static function () {
$this->assertSame( 'plugpress', $rows['analytics']['source'] );
}

/**
* Bridle and Bridle KB enrol themselves as `bridle` and `bridle-kb` (#338):
* PlugPress's, so live with nothing approved. Bridle's `bridle/` prefix must
* not pick up Bridle KB's `bridle-kb/` abilities, which share its start.
*/
public function test_bridle_and_bridle_kb_are_first_party_and_stay_apart() {
$add = static function ( $integrations ) {
$integrations['bridle'] = array(
'prefix' => 'bridle/',
'title' => 'Bridle',
);
$integrations['bridle-kb'] = array(
'prefix' => 'bridle-kb/',
'title' => 'Bridle KB',
);
return $integrations;
};
add_filter( 'saddle_integrations', $add );
$this->within_abilities_init(
static function () {
foreach ( array( 'bridle/list-conversations', 'bridle-kb/get-docs' ) as $name ) {
wp_register_ability(
$name,
array(
'label' => $name,
'description' => 'x',
'category' => 'saddle',
'input_schema' => array(
'type' => 'object',
'default' => (object) array(),
'properties' => (object) array(),
),
'execute_callback' => '__return_empty_array',
'permission_callback' => '__return_true',
'meta' => array( 'annotations' => array( 'readonly' => true ) ),
)
);
}
Saddle_Integrations::register_wrappers();
}
);
$rows = array_column( Saddle_Integrations::listing(), null, 'slug' );
remove_filter( 'saddle_integrations', $add );

$this->assertSame( array(), get_option( Saddle_Integrations::APPROVED_OPTION, array() ) );
$this->assertNotNull( wp_get_ability( 'saddle/bridle-list-conversations' ), 'Bridle is live with nothing approved.' );
$this->assertNotNull( wp_get_ability( 'saddle/bridle-kb-get-docs' ), 'Bridle KB is live with nothing approved.' );
$this->assertSame( 'plugpress', $rows['bridle']['source'] );
$this->assertSame( 'plugpress', $rows['bridle-kb']['source'] );
$this->assertSame( 1, $rows['bridle']['tools'], 'Bridle counts only its own bridle/* abilities.' );
$this->assertSame( 1, $rows['bridle-kb']['tools'] );
}

/**
* An empty prefix would wrap every ability on the site; `saddle/` and
* `core/` would re-expose abilities that aren't the partner's to offer.
Expand Down
Loading