Implementation

Turning This Into Reusable WordPress Abilities

⏱ 18 min

Everything in this course so far has been a manual discipline: read the competing pages yourself, run the gain check yourself, check the E-E-A-T checklist against a draft yourself. That’s the right way to learn it, and it’s also exactly the kind of repeatable, structured check that’s worth turning into a reusable ability, one that surfaces a specific, structured audit of a draft against information gain and E-E-A-T signals, for a human editor to act on. This lesson builds that ability, and it’s deliberately, permanently an analysis tool, not an auto-fix tool.

What you'll learn in this lesson
How to build a content-quality audit ability
One input schema covering a post plus optional context about what competing content already covers.
How to structure a single prompt covering both information gain and E-E-A-T
Asking a model to assess both, and to return specific, structured findings rather than a vague score.
Why this ability stays read-only, permanently
And why "human-reviewed, not auto-applied" is a design decision, not a placeholder for a future version.
How this fits with the fact-checking ability from Lesson 4
Two distinct, composable analysis abilities feeding the same human editorial process.
Prerequisites

Course 1 for wp_register_ability(), Course 5 (or Course 6’s SEO Content Optimizer project) for wp_ai_client_prompt() patterns, and Lesson 4’s content-review/extract-claims ability, which this one is designed to sit alongside.

Step 1: Design the input and output shape

The audit needs the post’s actual content, a target keyword or topic to assess relevance against, and, optionally, a human-supplied summary of what the currently-ranking competing pages already cover, the same research a human would do manually in Lesson 2’s gain check. Supplying that context is optional because the ability should still return a useful E-E-A-T read even without it, information gain specifically needs that competitive context to mean anything.

// File: content-review/content-review.php
add_action( 'wp_abilities_api_init', function () {
	wp_register_ability(
		'content-review/audit-quality',
		array(
			'label'               => __( 'Audit post for information gain and E-E-A-T', 'content-review' ),
			'description'         => __( 'Analyzes a post against a target topic, and optionally against a summary of already-ranking competing content, returning an information gain assessment, E-E-A-T signal checks, and specific suggestions. Read-only, does not modify the post.', 'content-review' ),
			'category'            => 'content',
			'input_schema'        => array(
				'type'       => 'object',
				'properties' => array(
					'post_id'            => array( 'type' => 'integer' ),
					'target_topic'       => array( 'type' => 'string' ),
					'competing_summary'  => array(
						'type'        => 'string',
						'description' => 'Optional: a human-written summary of what the top currently-ranking pages for this topic already cover.',
					),
				),
				'required'   => array( 'post_id', 'target_topic' ),
			),
			'output_schema'       => array(
				'type'       => 'object',
				'properties' => array(
					'information_gain' => array( 'type' => 'string' ),
					'eeat_signals'      => array(
						'type'  => 'array',
						'items' => array(
							'type'       => 'object',
							'properties' => array(
								'component'  => array( 'type' => 'string' ),
								'assessment' => array( 'type' => 'string' ),
							),
						),
					),
					'suggestions' => array(
						'type'  => 'array',
						'items' => array( 'type' => 'string' ),
					),
				),
			),
			'permission_callback' => function ( $input ) {
				return current_user_can( 'edit_post', $input['post_id'] );
			},
			'execute_callback'    => 'content_review_audit_quality',
			'meta' => array(
				'annotations' => array( 'readonly' => true ),
				'mcp'         => array( 'public' => true ),
			),
		)
	);
} );

Step 2: The execute_callback

This is where the audit prompt gets built and sent through the PHP AI Client SDK, then parsed into the structured shape declared in output_schema.

// File: content-review/content-review.php
function content_review_audit_quality( $input ) {
	$post = get_post( $input['post_id'] );
	if ( ! $post ) {
		return new WP_Error( 'not_found', 'No post found with that ID.' );
	}

	$topic     = sanitize_text_field( $input['target_topic'] );
	$body      = wp_strip_all_tags( $post->post_content );
	$competing = ! empty( $input['competing_summary'] )
		? sanitize_textarea_field( $input['competing_summary'] )
		: 'No summary of competing content was provided.';

	$prompt = <<<PROMPT
Audit this draft for two things: information gain and E-E-A-T signals.

Topic: {$topic}

What competing, already-ranking pages on this topic cover:
{$competing}

Draft to audit:
{$body}

Part 1, information gain: state plainly whether this draft says something the competing
content described above does not already say, and name the specific gap or new angle if
one exists. If the draft mostly restates the competing content, say so directly.

Part 2, E-E-A-T signals: assess each of the four components separately, Experience,
Expertise, Authoritativeness, Trustworthiness, based only on what's visible in the draft
itself (a firsthand detail, a credential or technical depth, a citation or outbound link,
a named author or sourced claim). Note where a signal is present and where it's missing.

Part 3, suggestions: list specific, actionable changes that would improve information gain
or E-E-A-T, not generic advice.
PROMPT;

	$response = wp_ai_client_prompt( $prompt )
		->using_model_preference( 'anthropic/claude', 'openai/gpt', 'google/gemini' )
		->generate_text();

	if ( empty( $response ) ) {
		return new WP_Error( 'empty_response', 'The AI provider returned no audit.' );
	}

	return content_review_parse_audit( $response );
}

function content_review_parse_audit( $response ) {
	// Simple section-based parsing: split on the three part headers the prompt asked for.
	$sections = preg_split( '/Part\s*\d+[,:]/i', $response );
	$sections = array_values( array_filter( array_map( 'trim', $sections ) ) );

	$information_gain = $sections[0] ?? '';
	$eeat_raw         = $sections[1] ?? '';
	$suggestions_raw  = $sections[2] ?? '';

	$eeat_signals = array();
	foreach ( array( 'Experience', 'Expertise', 'Authoritativeness', 'Trustworthiness' ) as $component ) {
		if ( preg_match( '/' . preg_quote( $component, '/' ) . '[:\-]?\s*(.+?)(?=(Experience|Expertise|Authoritativeness|Trustworthiness)|$)/is', $eeat_raw, $m ) ) {
			$eeat_signals[] = array(
				'component'  => $component,
				'assessment' => trim( $m[1] ),
			);
		}
	}

	$suggestions = array();
	foreach ( explode( "\n", $suggestions_raw ) as $line ) {
		$line = trim( $line );
		if ( preg_match( '/^[-*\d.]+\s*(.+)/', $line, $m ) ) {
			$suggestions[] = $m[1];
		}
	}

	return array(
		'information_gain' => $information_gain,
		'eeat_signals'      => $eeat_signals,
		'suggestions'       => $suggestions,
	);
}

sanitize_textarea_field() handles the multi-line competing_summary input correctly, since sanitize_text_field() alone would collapse it to a single line. The parsing here, like Course 6’s SEO optimizer, is intentionally simple: it’s splitting on the structure the prompt itself asked for, not attempting general-purpose natural language parsing.

Step 3: Why this stays read-only, permanently

This ability never calls wp_update_post(), and that’s not a limitation to remove in a later version, it’s the entire point. An audit that flags “this draft lacks a named author bio” or “no outbound citation supports this claim” is exactly the kind of finding a human needs to act on with real judgment, adding a genuine author bio, finding and linking a real source. None of that is safely automatable, and auto-applying a model’s own suggested fix for a trustworthiness gap would just replace one unverified claim with another. meta.annotations.readonly reflects that this ability’s entire value is in surfacing the audit, not resolving it.

Don't build a companion 'auto-fix' ability without a human gate

It’s tempting to extend this into an ability that automatically rewrites the draft to address its own findings. If you do build that, make sure it still stops at a draft state a human reviews and explicitly approves before publish, never auto-applies to a live post. The whole premise of this course is that quality signals like real experience and real citations can’t be faked by the same model that flagged them missing.

Test it

wp-env run cli wp eval '
$result = wp_get_ability( "content-review/audit-quality" )->execute( array(
	"post_id"           => 42,
	"target_topic"      => "wordpress ai content workflows",
	"competing_summary" => "Top pages define AI content risk generically and recommend disclosure, none cite a specific editorial workflow or named author.",
) );
print_r( $result );
'

You should see a non-empty information_gain assessment referencing your actual draft and the competing summary you provided, an eeat_signals array covering all four components, and a suggestions array with specific, actionable items, not generic filler.

Recap

The content-review/audit-quality ability combines the two manual disciplines from earlier in this course, the Lesson 2 gain check and the Lesson 3 E-E-A-T checklist, into one structured, repeatable audit backed by wp_ai_client_prompt(). It takes a post, a topic, and optional competing-content context, and returns a specific information gain read, a per-component E-E-A-T assessment, and concrete suggestions. It stays permanently read-only alongside Lesson 4’s content-review/extract-claims ability, both exist to hand a human editor a clearer, more structured starting point, neither one replaces the editorial judgment this whole course has argued is where quality actually comes from.

Resources & further reading

← Optimizing to Be Cited by AI Overviews and AI Search Engines Course Recap: An AI Content Workflow That Actually Ranks →