Skip to content
Open
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
84 changes: 62 additions & 22 deletions src/abilities/user-interface/abilities-integration.php
Original file line number Diff line number Diff line change
Expand Up @@ -411,8 +411,6 @@ private function get_post_identifier_input_schema(): array {
* @return array<string, mixed> The input schema.
*/
private function get_update_post_seo_data_input_schema(): array {
$nullable_string = [ 'type' => [ 'string', 'null' ] ];

return [
'type' => 'object',
'additionalProperties' => false,
Expand All @@ -426,23 +424,41 @@ private function get_update_post_seo_data_input_schema(): array {
'type' => 'string',
'description' => \__( 'The permalink (URL) of the post to update.', 'wordpress-seo' ),
],
'canonical' => $nullable_string,
'is_cornerstone' => [ 'type' => 'boolean' ],
'canonical' => [
'type' => [ 'string', 'null' ],
'description' => \__( 'The custom canonical URL for the post. Use null or an empty string to remove it and fall back to the default canonical.', 'wordpress-seo' ),
],
'is_cornerstone' => [
'type' => 'boolean',
'description' => \__( 'Whether the post is marked as cornerstone content.', 'wordpress-seo' ),
],
'noindex' => [
'type' => [ 'boolean', 'null' ],
'description' => \__( 'Whether search engines should be told not to index this post. true sets noindex (the post is excluded from search results); false forces the post to be indexed; null clears the setting and falls back to the post-type default.', 'wordpress-seo' ),
],
'nofollow' => [ 'type' => 'boolean' ],
'noimageindex' => [ 'type' => 'boolean' ],
'noarchive' => [ 'type' => 'boolean' ],
'nosnippet' => [ 'type' => 'boolean' ],
'nofollow' => [
'type' => 'boolean',
'description' => \__( 'Whether search engines should be told not to follow the links on this post.', 'wordpress-seo' ),
],
'noimageindex' => [
'type' => 'boolean',
'description' => \__( 'Whether search engines should be told not to index the images on this post.', 'wordpress-seo' ),
],
'noarchive' => [
'type' => 'boolean',
'description' => \__( 'Whether search engines should be told not to show a cached copy of this post.', 'wordpress-seo' ),
],
'nosnippet' => [
'type' => 'boolean',
'description' => \__( 'Whether search engines should be told not to show a snippet of this post in the search results.', 'wordpress-seo' ),
],
'schema_page_type' => $this->nullable_enum_schema(
\array_keys( Schema_Types::PAGE_TYPES ),
\__( 'The Schema.org page type for the post. Must be one of the supported page types. Use null to clear it and fall back to the default.', 'wordpress-seo' ),
\__( 'The Schema.org page type for the post. Must be one of the supported page types. Use null or an empty string to clear it and fall back to the default.', 'wordpress-seo' ),
),
'schema_article_type' => $this->nullable_enum_schema(
$this->get_schema_article_types(),
\__( 'The Schema.org article type for the post. Must be one of the supported article types. Use null to clear it and fall back to the default.', 'wordpress-seo' ),
\__( 'The Schema.org article type for the post. Must be one of the supported article types. Use null or an empty string to clear it and fall back to the default.', 'wordpress-seo' ),
),
],
];
Expand Down Expand Up @@ -519,6 +535,18 @@ private function get_post_seo_data_output_schema(): array {
];
};

// The raw side of a rendered pair carries the stored per-post value; null means "no custom value set", not "nothing is output".
$raw = static function ( $field ) {
return [
'type' => [ 'string', 'null' ],
'description' => \sprintf(
/* translators: %s expands to the name of the SEO field, e.g. "SEO title". */
\__( 'The custom %s as stored for the post, which may contain unexpanded replacement variables. Null when no custom value is set; the rendered companion field carries what is actually output.', 'wordpress-seo' ),
$field,
),
];
};

return [
'type' => 'object',
'properties' => [

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there a reason we don't keep the title/description format here?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah that was on purpose.

Output descriptions are not added when the keys are pretty self-explanatory and adding them would add length without adding information. I've seen agents reading both schemas when deciding how to use each ability so not adding self-explanatory stuff probably makes our abilities a bit more friendly to agent token costs.

The same keys have descriptions in the input because it's arguably more important for agents to understand what to send when using the abilities than the final results.

I'd keep as is and we can revisit in the future.

Expand All @@ -527,13 +555,19 @@ private function get_post_seo_data_output_schema(): array {
'permalink' => $nullable_string,
'post_type' => [ 'type' => 'string' ],
'post_status' => $nullable_string,
'seo_title' => $nullable_string,
'seo_title' => $raw( \__( 'SEO title', 'wordpress-seo' ) ),
'seo_title_rendered' => $rendered( \__( 'SEO title', 'wordpress-seo' ) ),
'meta_description' => $nullable_string,
'meta_description' => $raw( \__( 'meta description', 'wordpress-seo' ) ),
'meta_description_rendered' => $rendered( \__( 'meta description', 'wordpress-seo' ) ),
'focus_keyphrase' => $nullable_string,
'canonical' => $nullable_string,
'canonical_rendered' => $rendered( \__( 'canonical URL', 'wordpress-seo' ) ),
'canonical' => [
'type' => [ 'string', 'null' ],
'description' => \__( 'The custom canonical URL as stored for the post. Null when no custom value is set; the rendered companion field carries what is actually output.', 'wordpress-seo' ),
],
'canonical_rendered' => [
'type' => [ 'string', 'null' ],
'description' => \__( 'The canonical URL as output on the front end: the custom value when set, otherwise the permalink of the post. Null when nothing is output.', 'wordpress-seo' ),
],
'is_cornerstone' => [ 'type' => 'boolean' ],
'noindex' => [
'type' => [ 'boolean', 'null' ],
Expand All @@ -543,16 +577,22 @@ private function get_post_seo_data_output_schema(): array {
'noimageindex' => [ 'type' => 'boolean' ],
'noarchive' => [ 'type' => 'boolean' ],
'nosnippet' => [ 'type' => 'boolean' ],
'open_graph_title' => $nullable_string,
'open_graph_title' => $raw( \__( 'Open Graph title', 'wordpress-seo' ) ),
'open_graph_title_rendered' => $rendered( \__( 'Open Graph title', 'wordpress-seo' ) ),
'open_graph_description' => $nullable_string,
'open_graph_description' => $raw( \__( 'Open Graph description', 'wordpress-seo' ) ),
'open_graph_description_rendered' => $rendered( \__( 'Open Graph description', 'wordpress-seo' ) ),
'twitter_title' => $nullable_string,
'twitter_title_rendered' => $rendered( \__( 'Twitter title', 'wordpress-seo' ) ),
'twitter_description' => $nullable_string,
'twitter_description_rendered' => $rendered( \__( 'Twitter description', 'wordpress-seo' ) ),
'schema_page_type' => $nullable_string,
'schema_article_type' => $nullable_string,
'twitter_title' => $raw( \__( 'X title', 'wordpress-seo' ) ),
'twitter_title_rendered' => $rendered( \__( 'X title', 'wordpress-seo' ) ),
'twitter_description' => $raw( \__( 'X description', 'wordpress-seo' ) ),
'twitter_description_rendered' => $rendered( \__( 'X description', 'wordpress-seo' ) ),
'schema_page_type' => [
'type' => [ 'string', 'null' ],
'description' => \__( 'The Schema.org page type stored for the post. Null means no override is set and the default for the post type applies.', 'wordpress-seo' ),
],
'schema_article_type' => [
'type' => [ 'string', 'null' ],
'description' => \__( 'The Schema.org article type stored for the post. Null means no override is set and the default for the post type applies.', 'wordpress-seo' ),
],
'seo_score' => $score( \__( 'SEO analysis', 'wordpress-seo' ) ),
'readability_score' => $score( \__( 'readability analysis', 'wordpress-seo' ) ),
'inclusive_language_score' => $score( \__( 'inclusive language analysis', 'wordpress-seo' ) ),
Expand Down
81 changes: 59 additions & 22 deletions tests/Unit/Abilities/User_Interface/Abilities_Integration_Test.php
Original file line number Diff line number Diff line change
Expand Up @@ -551,8 +551,6 @@ private function get_expected_identifier_input_schema(): array {
* @return array<string, mixed> The schema.
*/
private function get_expected_update_input_schema(): array {
$nullable_string = [ 'type' => [ 'string', 'null' ] ];

return [
'type' => 'object',
'additionalProperties' => false,
Expand All @@ -566,24 +564,42 @@ private function get_expected_update_input_schema(): array {
'type' => 'string',
'description' => 'The permalink (URL) of the post to update.',
],
'canonical' => $nullable_string,
'is_cornerstone' => [ 'type' => 'boolean' ],
'canonical' => [
'type' => [ 'string', 'null' ],
'description' => 'The custom canonical URL for the post. Use null or an empty string to remove it and fall back to the default canonical.',
],
'is_cornerstone' => [
'type' => 'boolean',
'description' => 'Whether the post is marked as cornerstone content.',
],
'noindex' => [
'type' => [ 'boolean', 'null' ],
'description' => 'Whether search engines should be told not to index this post. true sets noindex (the post is excluded from search results); false forces the post to be indexed; null clears the setting and falls back to the post-type default.',
],
'nofollow' => [ 'type' => 'boolean' ],
'noimageindex' => [ 'type' => 'boolean' ],
'noarchive' => [ 'type' => 'boolean' ],
'nosnippet' => [ 'type' => 'boolean' ],
'nofollow' => [
'type' => 'boolean',
'description' => 'Whether search engines should be told not to follow the links on this post.',
],
'noimageindex' => [
'type' => 'boolean',
'description' => 'Whether search engines should be told not to index the images on this post.',
],
'noarchive' => [
'type' => 'boolean',
'description' => 'Whether search engines should be told not to show a cached copy of this post.',
],
'nosnippet' => [
'type' => 'boolean',
'description' => 'Whether search engines should be told not to show a snippet of this post in the search results.',
],
'schema_page_type' => [
'type' => [ 'string', 'null' ],
'description' => 'The Schema.org page type for the post. Must be one of the supported page types. Use null to clear it and fall back to the default.',
'description' => 'The Schema.org page type for the post. Must be one of the supported page types. Use null or an empty string to clear it and fall back to the default.',
'enum' => \array_merge( \array_keys( Schema_Types::PAGE_TYPES ), [ '', null ] ),
],
'schema_article_type' => [
'type' => [ 'string', 'null' ],
'description' => 'The Schema.org article type for the post. Must be one of the supported article types. Use null to clear it and fall back to the default.',
'description' => 'The Schema.org article type for the post. Must be one of the supported article types. Use null or an empty string to clear it and fall back to the default.',
'enum' => \array_merge( \array_keys( Schema_Types::ARTICLE_TYPES ), [ '', null ] ),
],
],
Expand Down Expand Up @@ -616,6 +632,15 @@ private function get_expected_output_schema(): array {
),
];
};
$raw = static function ( $field ) {
return [
'type' => [ 'string', 'null' ],
'description' => \sprintf(
'The custom %s as stored for the post, which may contain unexpanded replacement variables. Null when no custom value is set; the rendered companion field carries what is actually output.',
$field,
),
];
};

return [
'type' => 'object',
Expand All @@ -625,13 +650,19 @@ private function get_expected_output_schema(): array {
'permalink' => $nullable_string,
'post_type' => [ 'type' => 'string' ],
'post_status' => $nullable_string,
'seo_title' => $nullable_string,
'seo_title' => $raw( 'SEO title' ),
'seo_title_rendered' => $rendered( 'SEO title' ),
'meta_description' => $nullable_string,
'meta_description' => $raw( 'meta description' ),
'meta_description_rendered' => $rendered( 'meta description' ),
'focus_keyphrase' => $nullable_string,
'canonical' => $nullable_string,
'canonical_rendered' => $rendered( 'canonical URL' ),
'canonical' => [
'type' => [ 'string', 'null' ],
'description' => 'The custom canonical URL as stored for the post. Null when no custom value is set; the rendered companion field carries what is actually output.',
],
'canonical_rendered' => [
'type' => [ 'string', 'null' ],
'description' => 'The canonical URL as output on the front end: the custom value when set, otherwise the permalink of the post. Null when nothing is output.',
],
'is_cornerstone' => [ 'type' => 'boolean' ],
'noindex' => [
'type' => [ 'boolean', 'null' ],
Expand All @@ -641,16 +672,22 @@ private function get_expected_output_schema(): array {
'noimageindex' => [ 'type' => 'boolean' ],
'noarchive' => [ 'type' => 'boolean' ],
'nosnippet' => [ 'type' => 'boolean' ],
'open_graph_title' => $nullable_string,
'open_graph_title' => $raw( 'Open Graph title' ),
'open_graph_title_rendered' => $rendered( 'Open Graph title' ),
'open_graph_description' => $nullable_string,
'open_graph_description' => $raw( 'Open Graph description' ),
'open_graph_description_rendered' => $rendered( 'Open Graph description' ),
'twitter_title' => $nullable_string,
'twitter_title_rendered' => $rendered( 'Twitter title' ),
'twitter_description' => $nullable_string,
'twitter_description_rendered' => $rendered( 'Twitter description' ),
'schema_page_type' => $nullable_string,
'schema_article_type' => $nullable_string,
'twitter_title' => $raw( 'X title' ),
'twitter_title_rendered' => $rendered( 'X title' ),
'twitter_description' => $raw( 'X description' ),
'twitter_description_rendered' => $rendered( 'X description' ),
'schema_page_type' => [
'type' => [ 'string', 'null' ],
'description' => 'The Schema.org page type stored for the post. Null means no override is set and the default for the post type applies.',
],
'schema_article_type' => [
'type' => [ 'string', 'null' ],
'description' => 'The Schema.org article type stored for the post. Null means no override is set and the default for the post type applies.',
],
'seo_score' => $score( 'SEO analysis' ),
'readability_score' => $score( 'readability analysis' ),
'inclusive_language_score' => $score( 'inclusive language analysis' ),
Expand Down