{
	"$schema": "https://playground.wordpress.net/blueprint-schema.json",
	"meta": {
		"title": "Advanced Query Loop demo (standalone)",
		"description": "AQL with seeded demo content and a feature guide per inspector panel. Fully self-contained.",
		"author": "ryanwelcher"
	},
	"login": true,
	"landingPage": "/wp-admin/edit.php?post_type=aql_guide",
	"preferredVersions": {
		"php": "8.2",
		"wp": "latest"
	},
	"extraLibraries": [
		"wp-cli"
	],
	"steps": [
		{
			"step": "installPlugin",
			"pluginData": {
				"resource": "wordpress.org/plugins",
				"slug": "advanced-query-loop"
			},
			"options": {
				"activate": true
			}
		},
		{
			"step": "mkdir",
			"path": "/wordpress/wp-content/plugins/advanced-query-loop-testing"
		},
		{
			"step": "mkdir",
			"path": "/wordpress/wp-content/plugins/advanced-query-loop-testing/guides"
		},
		{
			"step": "writeFile",
			"path": "/wordpress/wp-content/plugins/advanced-query-loop-testing/plugin.php",
			"data": "<?php\n/**\n * Plugin Name: AQL Demo Dataset\n * Description: Registers and seeds deterministic content for Advanced Query Loop demos.\n * Version: 1.0.0\n */\n\ndefined( 'ABSPATH' ) || exit;\n\nadd_action( 'init', 'aql_demo_register_content' );\n\n/** Register demo post types, taxonomies, and REST-visible meta. */\nfunction aql_demo_register_content() {\n\tregister_post_type(\n\t\t'aql_project',\n\t\tarray(\n\t\t\t'labels'       => array( 'name' => 'Projects', 'singular_name' => 'Project' ),\n\t\t\t'public'       => true,\n\t\t\t'show_in_rest' => true,\n\t\t\t'has_archive'  => true,\n\t\t\t'rewrite'      => array( 'slug' => 'projects' ),\n\t\t\t'menu_icon'    => 'dashicons-portfolio',\n\t\t\t'supports'     => array( 'title', 'editor', 'excerpt', 'thumbnail', 'author', 'comments', 'custom-fields', 'page-attributes' ),\n\t\t\t'taxonomies'   => array( 'category', 'post_tag' ),\n\t\t)\n\t);\n\n\tregister_post_type(\n\t\t'aql_guide',\n\t\tarray(\n\t\t\t'labels'       => array( 'name' => 'Guides', 'singular_name' => 'Guide' ),\n\t\t\t'public'       => true,\n\t\t\t'show_in_rest' => true,\n\t\t\t'has_archive'  => true,\n\t\t\t'hierarchical' => true,\n\t\t\t'rewrite'      => array( 'slug' => 'guides' ),\n\t\t\t'menu_icon'    => 'dashicons-welcome-learn-more',\n\t\t\t'supports'     => array( 'title', 'editor', 'excerpt', 'author', 'custom-fields', 'page-attributes' ),\n\t\t)\n\t);\n\n\tregister_taxonomy(\n\t\t'aql_topic',\n\t\tarray( 'post', 'aql_project', 'aql_guide' ),\n\t\tarray(\n\t\t\t'labels'            => array( 'name' => 'Topics', 'singular_name' => 'Topic' ),\n\t\t\t'public'            => true,\n\t\t\t'hierarchical'      => true,\n\t\t\t'show_in_rest'      => true,\n\t\t\t'show_admin_column' => true,\n\t\t\t'rewrite'           => array( 'slug' => 'topic' ),\n\t\t)\n\t);\n\n\tregister_taxonomy(\n\t\t'aql_audience',\n\t\tarray( 'post', 'aql_project', 'aql_guide' ),\n\t\tarray(\n\t\t\t'labels'            => array( 'name' => 'Audiences', 'singular_name' => 'Audience' ),\n\t\t\t'public'            => true,\n\t\t\t'hierarchical'      => false,\n\t\t\t'show_in_rest'      => true,\n\t\t\t'show_admin_column' => true,\n\t\t\t'rewrite'           => array( 'slug' => 'audience' ),\n\t\t)\n\t);\n\n\t$meta = array(\n\t\t'aql_demo_code'       => 'string',\n\t\t'aql_demo_score'      => 'integer',\n\t\t'aql_demo_price'      => 'number',\n\t\t'aql_demo_rating'     => 'number',\n\t\t'aql_demo_date'       => 'string',\n\t\t'aql_demo_datetime'   => 'string',\n\t\t'aql_demo_time'       => 'string',\n\t\t'aql_demo_priority'   => 'integer',\n\t\t'aql_demo_status'     => 'string',\n\t\t'aql_demo_featured'   => 'boolean',\n\t\t'aql_demo_optional'   => 'string',\n\t\t'aql_demo_sort_label' => 'string',\n\t);\n\n\tforeach ( array( 'post', 'aql_project', 'aql_guide' ) as $post_type ) {\n\t\tforeach ( $meta as $key => $type ) {\n\t\t\tregister_post_meta(\n\t\t\t\t$post_type,\n\t\t\t\t$key,\n\t\t\t\tarray(\n\t\t\t\t\t'show_in_rest'  => true,\n\t\t\t\t\t'single'        => true,\n\t\t\t\t\t'type'          => $type,\n\t\t\t\t\t'auth_callback' => static function() {\n\t\t\t\t\t\treturn current_user_can( 'edit_posts' );\n\t\t\t\t\t},\n\t\t\t\t)\n\t\t\t);\n\t\t}\n\t}\n}\n\nadd_action( 'init', 'aql_demo_register_post_type_block' );\n\n/** Register a tiny dynamic block that prints the current post's type label. */\nfunction aql_demo_register_post_type_block() {\n\tregister_block_type(\n\t\t'aql-demo/post-type',\n\t\tarray(\n\t\t\t'api_version'     => 3,\n\t\t\t'title'           => 'Post Type Label',\n\t\t\t'uses_context'    => array( 'postId' ),\n\t\t\t'render_callback' => static function ( $attributes, $content, $block ) {\n\t\t\t\t$post_id   = $block->context['postId'] ?? get_the_ID();\n\t\t\t\t$post_type = get_post_type_object( get_post_type( $post_id ) );\n\t\t\t\tif ( ! $post_type ) {\n\t\t\t\t\treturn '';\n\t\t\t\t}\n\t\t\t\treturn sprintf(\n\t\t\t\t\t'<div %s>%s</div>',\n\t\t\t\t\tget_block_wrapper_attributes( array( 'class' => 'aql-demo-post-type' ) ),\n\t\t\t\t\tesc_html( $post_type->labels->singular_name )\n\t\t\t\t);\n\t\t\t},\n\t\t)\n\t);\n}\n\nadd_action( 'enqueue_block_editor_assets', 'aql_demo_post_type_block_editor_script' );\n\n/** Editor-side registration so the block previews instead of showing as missing. */\nfunction aql_demo_post_type_block_editor_script() {\n\twp_register_script( 'aql-demo-blocks', false, array( 'wp-blocks', 'wp-element', 'wp-block-editor' ), '1.0.0', true );\n\twp_enqueue_script( 'aql-demo-blocks' );\n\twp_add_inline_script(\n\t\t'aql-demo-blocks',\n\t\t\"( function ( blocks, element, blockEditor ) {\n\t\t\tblocks.registerBlockType( 'aql-demo/post-type', {\n\t\t\t\ttitle: 'Post Type Label',\n\t\t\t\ticon: 'admin-post',\n\t\t\t\tcategory: 'theme',\n\t\t\t\tusesContext: [ 'postType' ],\n\t\t\t\tedit: function ( props ) {\n\t\t\t\t\treturn element.createElement(\n\t\t\t\t\t\t'div',\n\t\t\t\t\t\tblockEditor.useBlockProps(),\n\t\t\t\t\t\tprops.context.postType || 'Post type'\n\t\t\t\t\t);\n\t\t\t\t},\n\t\t\t\tsave: function () {\n\t\t\t\t\treturn null;\n\t\t\t\t},\n\t\t\t} );\n\t\t} )( window.wp.blocks, window.wp.element, window.wp.blockEditor );\"\n\t);\n}\n\nif ( defined( 'WP_CLI' ) && WP_CLI ) {\n\trequire_once __DIR__ . '/seed-command.php';\n}\n"
		},
		{
			"step": "writeFile",
			"path": "/wordpress/wp-content/plugins/advanced-query-loop-testing/seed-command.php",
			"data": "<?php\n/** WP-CLI commands for the AQL demo dataset. */\n\ndefined( 'ABSPATH' ) || exit;\n\nclass AQL_Demo_Command {\n\tconst OPTION = 'aql_demo_dataset_manifest';\n\n\t/** Create the complete demo dataset. */\n\tpublic function seed() {\n\t\tif ( get_option( self::OPTION ) ) {\n\t\t\tWP_CLI::warning( 'AQL demo data already exists. Run `wp aql-demo reset` first to rebuild it.' );\n\t\t\treturn;\n\t\t}\n\n\t\taql_demo_register_content();\n\t\t$manifest = array( 'posts' => array(), 'users' => array(), 'terms' => array() );\n\t\t$users    = $this->create_users( $manifest );\n\t\t$terms    = $this->create_terms( $manifest );\n\n\t\t$post_dates = array(\n\t\t\t'-1 day', 'now', 'first day of this month 09:00', '-6 days', '-12 days', '-20 days',\n\t\t\t'first day of last month 09:00', 'last day of last month 18:00', '-1 month -8 days', '-1 month -16 days',\n\t\t\t'-2 months', '-2 months -12 days', '-3 months', '-3 months -10 days', '-4 months', '-5 months',\n\t\t\t'-6 months', '-6 months -10 days', '-8 months', '-10 months', '-12 months', '-13 months', '+1 day', '+30 days',\n\t\t);\n\t\t$post_titles = array(\n\t\t\t'Zebra Blocks', 'Atlas Query', 'Nimbus REST', 'Birch Patterns',\n\t\t\t'Quartz Cache', 'Cedar Editorial', 'Lumen Design', 'Harbor Search',\n\t\t\t'Orbit Fields', 'Delta Filters', 'Maple Taxonomy', 'Echo Templates',\n\t\t\t'Willow Authors', 'Kite Metadata', 'Juniper Dates', 'Fjord Pagination',\n\t\t\t'Grove Archives', 'Pixel Ordering', 'Summit Comments', 'Raven Includes',\n\t\t\t'Beacon Excludes', 'Mango Performance', 'Future Blocks', 'Horizon Roadmap',\n\t\t);\n\t\t$posts = array();\n\t\tforeach ( $post_titles as $index => $title ) {\n\t\t\t$posts[] = $this->create_item(\n\t\t\t\t'post', $title, 'article-' . sprintf( '%02d', 24 - $index ), $post_dates[ $index ],\n\t\t\t\t$users[ $index % 3 ], ( $index * 7 ) % 25, $index, $terms, $manifest\n\t\t\t);\n\t\t}\n\n\t\t$project_titles = array(\n\t\t\t'Zebra Migration', 'Atlas Redesign', 'Nimbus API', 'Birch Blocks',\n\t\t\t'Quartz Cache', 'Cedar Editorial', 'Lumen Portal', 'Harbor Toolkit',\n\t\t\t'Orbit Search', 'Delta Commerce', 'Maple Studio', 'Echo Platform',\n\t\t);\n\t\t$menu_orders   = array( 40, 10, 90, 20, 70, 30, 100, 50, 80, 60, 120, 110 );\n\t\t$projects      = array();\n\t\tforeach ( $project_titles as $index => $title ) {\n\t\t\t$projects[] = $this->create_item(\n\t\t\t\t'aql_project', $title, 'project-' . chr( 108 - $index ), $post_dates[ ( $index * 2 + 3 ) % 24 ],\n\t\t\t\t$users[ ( $index + 1 ) % 3 ], $menu_orders[ $index ], $index + 24, $terms, $manifest\n\t\t\t);\n\t\t}\n\n\t\t$this->create_comments( $posts, $projects );\n\t\t$manifest['version'] = 1;\n\t\t$manifest['counts']  = array( 'post' => 24, 'aql_project' => 12 );\n\t\tupdate_option( self::OPTION, $manifest, false );\n\t\tflush_rewrite_rules();\n\t\tWP_CLI::success( 'Imported 36 AQL demo items, 3 authors, 4 taxonomy sets, metadata, and comments. Run `wp aql-demo guides` to import the Guides.' );\n\t}\n\n\t/**\n\t * Seed the recipe posts the feature guides query: 12 posts in a Recipes\n\t * category with duplicated dates, a numeric 'rating' meta with ties and\n\t * gaps, a 'featured' flag on three, and noise meta on all — designed so\n\t * ordering, meta filters, and secondary sorts have visible work to do.\n\t * Idempotent: skips any recipe whose slug already exists.\n\t */\n\tpublic function recipes() {\n\t\t$recipes = array(\n\t\t\tarray( 'Apple Galette', '2024-06-01 10:00:00', '5', true ),\n\t\t\tarray( 'Banana Bread', '2024-06-01 10:00:00', '3', false ),\n\t\t\tarray( 'Cardamom Buns', '2024-06-01 10:00:00', null, false ),\n\t\t\tarray( 'Date Squares', '2024-03-15 09:00:00', '5', true ),\n\t\t\tarray( 'Espresso Brownies', '2024-03-15 09:00:00', '1', false ),\n\t\t\tarray( 'Focaccia', '2024-09-10 12:00:00', '3', true ),\n\t\t\tarray( 'Ginger Scones', '2024-09-10 12:00:00', null, false ),\n\t\t\tarray( 'Honey Cake', '2024-01-20 08:00:00', '4', false ),\n\t\t\tarray( 'Irish Soda Bread', '2024-11-05 16:00:00', '2', false ),\n\t\t\tarray( 'Jam Thumbprints', '2024-11-05 16:00:00', '2', false ),\n\t\t\tarray( 'Key Lime Pie', '2024-05-25 11:00:00', null, false ),\n\t\t\tarray( 'Lemon Loaf', '2024-08-14 14:00:00', '5', false ),\n\t\t);\n\n\t\t$cat    = wp_insert_term( 'Recipes', 'category', array( 'slug' => 'recipes' ) );\n\t\t$cat_id = is_wp_error( $cat ) ? get_term_by( 'slug', 'recipes', 'category' )->term_id : $cat['term_id'];\n\n\t\t$created = 0;\n\t\tforeach ( $recipes as list( $title, $date, $rating, $featured ) ) {\n\t\t\t$slug = sanitize_title( $title );\n\t\t\tif ( $this->find_post_id_by_slug( $slug, array( 'post' ) ) ) {\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\t$content = sprintf(\n\t\t\t\t'<p>%s%s</p>',\n\t\t\t\t$rating ? \"Reader rating: {$rating}/5.\" : 'Not yet rated.',\n\t\t\t\t$featured ? \" An editor's pick.\" : ''\n\t\t\t);\n\t\t\t$id      = wp_insert_post( array(\n\t\t\t\t'post_title'   => $title,\n\t\t\t\t'post_name'    => $slug,\n\t\t\t\t'post_content' => $content,\n\t\t\t\t'post_status'  => 'publish',\n\t\t\t\t'post_date'    => $date,\n\t\t\t) );\n\t\t\tif ( is_wp_error( $id ) || ! $id ) {\n\t\t\t\tWP_CLI::warning( \"Failed: {$title}\" );\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tupdate_post_meta( $id, '_test_noise', '1758000000:' . $id );\n\t\t\tif ( null !== $rating ) {\n\t\t\t\tupdate_post_meta( $id, 'rating', $rating );\n\t\t\t}\n\t\t\tif ( $featured ) {\n\t\t\t\tupdate_post_meta( $id, 'featured', 'yes' );\n\t\t\t}\n\t\t\twp_set_object_terms( $id, array( (int) $cat_id ), 'category', true );\n\t\t\t$created++;\n\t\t}\n\t\tWP_CLI::success( \"Recipes seeded: {$created} created, \" . ( count( $recipes ) - $created ) . ' already present.' );\n\t}\n\n\t/**\n\t * Import (or update) the feature guides from guides/manifest.json.\n\t *\n\t * Guide content lives in guides/*.html with portable ID tokens:\n\t *   %%POST_ID:slug%%          -> ID of the post/project/guide with that slug\n\t *   %%TERM_ID:taxonomy:slug%% -> term ID (created if missing, category only)\n\t * so the same files import cleanly on any environment (Studio, Playground).\n\t * Guides are matched by slug: re-running updates content in place.\n\t */\n\tpublic function guides() {\n\t\t$manifest_path = __DIR__ . '/guides/manifest.json';\n\t\tif ( ! file_exists( $manifest_path ) ) {\n\t\t\tWP_CLI::error( 'guides/manifest.json not found.' );\n\t\t}\n\t\t$entries = json_decode( file_get_contents( $manifest_path ), true );\n\t\tif ( ! is_array( $entries ) ) {\n\t\t\tWP_CLI::error( 'guides/manifest.json is not valid JSON.' );\n\t\t}\n\n\t\t$slug_to_id = array();\n\t\tforeach ( $entries as $entry ) {\n\t\t\t$file = __DIR__ . '/guides/' . $entry['file'];\n\t\t\tif ( ! file_exists( $file ) ) {\n\t\t\t\tWP_CLI::warning( \"Missing {$entry['file']} — skipped.\" );\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\t$content = $this->replace_guide_tokens( file_get_contents( $file ) );\n\t\t\t$parent  = 0;\n\t\t\tif ( ! empty( $entry['parent'] ) ) {\n\t\t\t\t$parent = $slug_to_id[ $entry['parent'] ] ?? $this->find_post_id_by_slug( $entry['parent'], array( 'aql_guide' ) );\n\t\t\t}\n\n\t\t\t$existing = get_posts( array( 'post_type' => 'aql_guide', 'name' => $entry['slug'], 'posts_per_page' => 1, 'post_status' => 'any' ) );\n\t\t\t$postarr  = array(\n\t\t\t\t'post_type'    => 'aql_guide',\n\t\t\t\t'post_status'  => 'publish',\n\t\t\t\t'post_title'   => $entry['title'],\n\t\t\t\t'post_name'    => $entry['slug'],\n\t\t\t\t'post_excerpt' => $entry['excerpt'] ?? '',\n\t\t\t\t'post_content' => $content,\n\t\t\t\t'post_parent'  => (int) $parent,\n\t\t\t\t'menu_order'   => (int) ( $entry['menu_order'] ?? 0 ),\n\t\t\t);\n\t\t\tif ( $existing ) {\n\t\t\t\t$postarr['ID'] = $existing[0]->ID;\n\t\t\t}\n\t\t\t$id = wp_insert_post( $postarr, true );\n\t\t\tif ( is_wp_error( $id ) ) {\n\t\t\t\tWP_CLI::warning( \"{$entry['slug']}: \" . $id->get_error_message() );\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\t$slug_to_id[ $entry['slug'] ] = $id;\n\t\t\tWP_CLI::log( ( $existing ? 'Updated' : 'Created' ) . \" guide #{$id}: {$entry['title']} (/{$entry['slug']})\" );\n\t\t}\n\t\tflush_rewrite_rules();\n\t\tWP_CLI::success( 'Guides imported: ' . count( $slug_to_id ) . '.' );\n\t}\n\n\t/** Resolve %%POST_ID:slug%% and %%TERM_ID:taxonomy:slug%% tokens. */\n\tprivate function replace_guide_tokens( $content ) {\n\t\t$content = preg_replace_callback(\n\t\t\t'/%%POST_ID:([a-z0-9-]+)%%/',\n\t\t\tfunction ( $m ) {\n\t\t\t\t$id = $this->find_post_id_by_slug( $m[1], array( 'post', 'aql_project', 'aql_guide', 'page' ) );\n\t\t\t\tif ( ! $id ) {\n\t\t\t\t\tWP_CLI::warning( \"Token POST_ID:{$m[1]} did not resolve.\" );\n\t\t\t\t}\n\t\t\t\treturn (string) $id;\n\t\t\t},\n\t\t\t$content\n\t\t);\n\t\treturn preg_replace_callback(\n\t\t\t'/%%TERM_ID:([a-z0-9_-]+):([a-z0-9-]+)%%/',\n\t\t\tfunction ( $m ) {\n\t\t\t\t$term = get_term_by( 'slug', $m[2], $m[1] );\n\t\t\t\tif ( ! $term && 'category' === $m[1] ) {\n\t\t\t\t\t$created = wp_insert_term( ucwords( str_replace( '-', ' ', $m[2] ) ), 'category', array( 'slug' => $m[2] ) );\n\t\t\t\t\t$term    = is_wp_error( $created ) ? false : get_term( $created['term_id'], 'category' );\n\t\t\t\t}\n\t\t\t\tif ( ! $term ) {\n\t\t\t\t\tWP_CLI::warning( \"Token TERM_ID:{$m[1]}:{$m[2]} did not resolve.\" );\n\t\t\t\t\treturn '0';\n\t\t\t\t}\n\t\t\t\treturn (string) $term->term_id;\n\t\t\t},\n\t\t\t$content\n\t\t);\n\t}\n\n\t/** Find a published post ID by slug across the given post types. */\n\tprivate function find_post_id_by_slug( $slug, $types ) {\n\t\t$found = get_posts( array( 'post_type' => $types, 'name' => $slug, 'posts_per_page' => 1, 'post_status' => 'any' ) );\n\t\treturn $found ? (int) $found[0]->ID : 0;\n\t}\n\n\t/** Delete only records created by this plugin. */\n\tpublic function reset() {\n\t\t$manifest = get_option( self::OPTION );\n\t\tif ( ! $manifest ) {\n\t\t\tWP_CLI::warning( 'No AQL demo manifest was found.' );\n\t\t\treturn;\n\t\t}\n\t\tforeach ( array_reverse( $manifest['posts'] ?? array() ) as $post_id ) {\n\t\t\twp_delete_post( $post_id, true );\n\t\t}\n\t\tforeach ( $manifest['terms'] ?? array() as $taxonomy => $ids ) {\n\t\t\tforeach ( array_reverse( $ids ) as $term_id ) {\n\t\t\t\twp_delete_term( $term_id, $taxonomy );\n\t\t\t}\n\t\t}\n\t\tforeach ( $manifest['users'] ?? array() as $user_id ) {\n\t\t\twp_delete_user( $user_id, 1 );\n\t\t}\n\t\tdelete_option( self::OPTION );\n\t\tflush_rewrite_rules();\n\t\tWP_CLI::success( 'Removed the AQL demo dataset.' );\n\t}\n\n\t/** Confirm all expected demo records and registered objects exist. */\n\tpublic function verify() {\n\t\t$manifest = get_option( self::OPTION );\n\t\tif ( ! $manifest ) {\n\t\t\tWP_CLI::error( 'Dataset is not seeded.' );\n\t\t}\n\t\t$expected = $manifest['counts'];\n\t\tforeach ( $expected as $type => $count ) {\n\t\t\t$actual = (int) wp_count_posts( $type )->publish;\n\t\t\tif ( $actual < $count ) {\n\t\t\t\tWP_CLI::error( \"$type expected at least $count published items; found $actual.\" );\n\t\t\t}\n\t\t\tWP_CLI::log( \"$type: $actual published (expected at least $count)\" );\n\t\t}\n\t\tforeach ( array( 'aql_topic', 'aql_audience' ) as $taxonomy ) {\n\t\t\tWP_CLI::log( \"$taxonomy: \" . wp_count_terms( array( 'taxonomy' => $taxonomy, 'hide_empty' => false ) ) . ' terms' );\n\t\t}\n\t\tWP_CLI::success( 'AQL demo dataset verified.' );\n\t}\n\n\tprivate function create_users( &$manifest ) {\n\t\t$definitions = array(\n\t\t\tarray( 'aql_ada', 'Ada Author', 'ada@aql-demo.test' ),\n\t\t\tarray( 'aql_bruno', 'Bruno Builder', 'bruno@aql-demo.test' ),\n\t\t\tarray( 'aql_cora', 'Cora Creator', 'cora@aql-demo.test' ),\n\t\t);\n\t\t$ids = array();\n\t\tforeach ( $definitions as $definition ) {\n\t\t\t$id = wp_create_user( $definition[0], wp_generate_password( 24 ), $definition[2] );\n\t\t\tif ( is_wp_error( $id ) ) {\n\t\t\t\t$id = username_exists( $definition[0] );\n\t\t\t}\n\t\t\twp_update_user( array( 'ID' => $id, 'display_name' => $definition[1], 'role' => 'author' ) );\n\t\t\t$ids[] = (int) $id;\n\t\t\t$manifest['users'][] = (int) $id;\n\t\t}\n\t\treturn $ids;\n\t}\n\n\tprivate function create_terms( &$manifest ) {\n\t\t$tree = array(\n\t\t\t'category' => array( 'Engineering' => array( 'WordPress', 'JavaScript' ), 'Design' => array( 'Accessibility' ), 'Business' => array() ),\n\t\t\t'post_tag' => array( 'featured' => array(), 'evergreen' => array(), 'experimental' => array(), 'beginner' => array(), 'advanced' => array() ),\n\t\t\t'aql_topic' => array( 'Development' => array( 'Blocks', 'REST API' ), 'Performance' => array( 'Caching' ), 'Editorial' => array() ),\n\t\t\t'aql_audience' => array( 'developers' => array(), 'designers' => array(), 'publishers' => array(), 'agencies' => array() ),\n\t\t);\n\t\t$result = array();\n\t\tforeach ( $tree as $taxonomy => $parents ) {\n\t\t\tforeach ( $parents as $name => $children ) {\n\t\t\t\t$parent = wp_insert_term( $name, $taxonomy );\n\t\t\t\t$parent_id = is_wp_error( $parent ) ? get_term_by( 'name', $name, $taxonomy )->term_id : $parent['term_id'];\n\t\t\t\t$result[ $taxonomy ][] = (int) $parent_id;\n\t\t\t\t$manifest['terms'][ $taxonomy ][] = (int) $parent_id;\n\t\t\t\tforeach ( $children as $child_name ) {\n\t\t\t\t\t$child = wp_insert_term( $child_name, $taxonomy, array( 'parent' => $parent_id ) );\n\t\t\t\t\t$child_id = is_wp_error( $child ) ? get_term_by( 'name', $child_name, $taxonomy )->term_id : $child['term_id'];\n\t\t\t\t\t$result[ $taxonomy ][] = (int) $child_id;\n\t\t\t\t\t$manifest['terms'][ $taxonomy ][] = (int) $child_id;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\treturn $result;\n\t}\n\n\tprivate function create_item( $type, $title, $slug, $relative_date, $author, $menu_order, $index, $terms, &$manifest, $parent = 0 ) {\n\t\t$timestamp = strtotime( $relative_date, current_time( 'timestamp' ) );\n\t\t$date      = wp_date( 'Y-m-d H:i:s', $timestamp );\n\t\t$id = wp_insert_post( array(\n\t\t\t'post_type' => $type, 'post_status' => 'publish', 'post_title' => $title, 'post_name' => $slug,\n\t\t\t'post_excerpt' => 'Deterministic Advanced Query Loop demo record.', 'post_author' => $author,\n\t\t\t'post_parent' => $parent, 'menu_order' => $menu_order, 'post_date' => min( $date, current_time( 'mysql' ) ),\n\t\t), true );\n\t\tif ( is_wp_error( $id ) ) {\n\t\t\tWP_CLI::error( $id->get_error_message() );\n\t\t}\n\t\tglobal $wpdb;\n\t\t$wpdb->update( $wpdb->posts, array( 'post_date' => $date, 'post_date_gmt' => get_gmt_from_date( $date ), 'post_status' => 'publish' ), array( 'ID' => $id ) );\n\t\t$modified = wp_date( 'Y-m-d H:i:s', strtotime( '-' . ( ( $index * 11 ) % 31 ) . ' hours', current_time( 'timestamp' ) ) );\n\t\t$wpdb->update( $wpdb->posts, array( 'post_modified' => $modified, 'post_modified_gmt' => get_gmt_from_date( $modified ) ), array( 'ID' => $id ) );\n\t\tclean_post_cache( $id );\n\n\t\t$codes   = array( 'ALPHA', 'BETA', 'GAMMA', 'DELTA' );\n\t\t$scores  = array( 5, 10, 20, 40, 80, 100 );\n\t\t$prices  = array( 9.5, 19.99, 49.0, 99.95 );\n\t\t$ratings = array( 1.5, 2.5, 3.5, 4.5, 5.0 );\n\t\t$labels  = array( 'Zebra', 'Apple', 'Mango', 'Birch' );\n\t\t$statuses = array( 'planned', 'active', 'paused', 'complete' );\n\t\t$values = array(\n\t\t\t'aql_demo_code' => $codes[ $index % 4 ], 'aql_demo_score' => $scores[ $index % 6 ],\n\t\t\t'aql_demo_price' => $prices[ $index % 4 ], 'aql_demo_rating' => $ratings[ $index % 5 ],\n\t\t\t'aql_demo_date' => wp_date( 'Y-m-d', $timestamp ), 'aql_demo_datetime' => $date,\n\t\t\t'aql_demo_time' => array( '08:00:00', '12:30:00', '18:45:00' )[ $index % 3 ],\n\t\t\t'aql_demo_priority' => ( $index % 5 ) + 1, 'aql_demo_status' => $statuses[ $index % 4 ],\n\t\t\t'aql_demo_featured' => 0 === $index % 3, 'aql_demo_sort_label' => $labels[ $index % 4 ],\n\t\t);\n\t\tif ( 0 === $index % 2 ) {\n\t\t\t$values['aql_demo_optional'] = 'present-' . $index;\n\t\t}\n\t\tforeach ( $values as $key => $value ) {\n\t\t\tupdate_post_meta( $id, $key, $value );\n\t\t}\n\n\t\twp_set_object_terms( $id, array( $terms['aql_topic'][ $index % count( $terms['aql_topic'] ) ] ), 'aql_topic' );\n\t\tif ( 0 !== $index % 5 ) {\n\t\t\twp_set_object_terms( $id, array( $terms['aql_audience'][ $index % 4 ] ), 'aql_audience' );\n\t\t}\n\t\tif ( in_array( $type, array( 'post', 'aql_project' ), true ) ) {\n\t\t\twp_set_object_terms( $id, array( $terms['category'][ $index % count( $terms['category'] ) ] ), 'category' );\n\t\t\twp_set_object_terms( $id, array( $terms['post_tag'][ $index % 5 ] ), 'post_tag' );\n\t\t}\n\t\t$manifest['posts'][] = (int) $id;\n\t\treturn (int) $id;\n\t}\n\n\tprivate function create_comments( $posts, $projects ) {\n\t\t$targets = array_merge( array_slice( $posts, 0, 5 ), array_slice( $projects, 0, 1 ) );\n\t\t$counts  = array( 0, 1, 2, 5, 8, 13 );\n\t\tforeach ( $targets as $position => $post_id ) {\n\t\t\tfor ( $i = 0; $i < $counts[ $position ]; $i++ ) {\n\t\t\t\twp_insert_comment( array(\n\t\t\t\t\t'comment_post_ID' => $post_id, 'comment_author' => 'Demo Reader ' . ( $i + 1 ),\n\t\t\t\t\t'comment_author_email' => 'reader' . $position . '-' . $i . '@aql-demo.test',\n\t\t\t\t\t'comment_content' => 'Approved AQL demo comment ' . ( $i + 1 ) . '.', 'comment_approved' => 1,\n\t\t\t\t) );\n\t\t\t}\n\t\t}\n\t}\n}\n\nWP_CLI::add_command( 'aql-demo', 'AQL_Demo_Command' );\n"
		},
		{
			"step": "writeFile",
			"path": "/wordpress/wp-content/plugins/advanced-query-loop-testing/guides/manifest.json",
			"data": "[\n\t{ \"file\": \"start-here.html\", \"slug\": \"start-here\", \"title\": \"Start Here\", \"menu_order\": 10, \"excerpt\": \"What the Advanced Query Loop block is, how to add it, and where its settings live.\" },\n\t{ \"file\": \"post-types.html\", \"slug\": \"post-types-and-picking-posts\", \"title\": \"Post Types & Picking Posts\", \"parent\": \"start-here\", \"menu_order\": 20, \"excerpt\": \"Mix content types in one list, hand-pick posts, leave posts out, and show only child items with the AQL: Post panel.\" },\n\t{ \"file\": \"order-by.html\", \"slug\": \"order-by\", \"title\": \"Order By\", \"parent\": \"start-here\", \"menu_order\": 30, \"excerpt\": \"Set the order posts appear in with the AQL: Order by panel — by property, by custom field, and with a secondary sort for ties.\" },\n\t{ \"file\": \"dates.html\", \"slug\": \"dates\", \"title\": \"Dates\", \"parent\": \"start-here\", \"menu_order\": 40, \"excerpt\": \"Show recent posts automatically or everything before or after a date with the AQL: Date panel.\" },\n\t{ \"file\": \"custom-fields.html\", \"slug\": \"custom-fields\", \"title\": \"Custom Fields\", \"parent\": \"start-here\", \"menu_order\": 50, \"excerpt\": \"Filter posts by their custom fields — alone or combined — with the AQL: Meta panel.\" },\n\t{ \"file\": \"categories-and-tags.html\", \"slug\": \"categories-and-tags\", \"title\": \"Categories & Tags\", \"parent\": \"start-here\", \"menu_order\": 60, \"excerpt\": \"Filter by categories, tags, and custom taxonomies with the AQL: Taxonomy panel.\" },\n\t{ \"file\": \"performance.html\", \"slug\": \"performance\", \"title\": \"Performance\", \"parent\": \"start-here\", \"menu_order\": 70, \"excerpt\": \"Skip pagination you don't need and cache query results with the AQL: Performance panel.\" },\n\t{ \"file\": \"query-id.html\", \"slug\": \"query-id\", \"title\": \"Query ID\", \"parent\": \"start-here\", \"menu_order\": 80, \"excerpt\": \"Name a query so a developer can target it with code, using the AQL: Advanced panel.\" },\n\t{ \"file\": \"query-basics.html\", \"slug\": \"query-basics\", \"title\": \"Query Basics: Mixed Content\", \"parent\": \"start-here\", \"menu_order\": 90, \"excerpt\": \"A combined recipe: Posts and Projects together in one query, sorted and labeled.\" }\n]\n"
		},
		{
			"step": "writeFile",
			"path": "/wordpress/wp-content/plugins/advanced-query-loop-testing/guides/categories-and-tags.html",
			"data": "<!-- wp:group {\"align\":\"wide\",\"backgroundColor\":\"accent-5\",\"layout\":{\"type\":\"constrained\"}} -->\n<div class=\"wp-block-group alignwide has-accent-5-background-color has-background\"><!-- wp:paragraph {\"textColor\":\"accent-4\",\"fontSize\":\"small\"} -->\n<p class=\"has-accent-4-color has-text-color has-small-font-size\">AQL GUIDE · FOUNDATIONS</p>\n<!-- /wp:paragraph -->\n\n<!-- wp:heading {\"level\":2,\"fontSize\":\"xx-large\"} -->\n<h2 class=\"wp-block-heading has-xx-large-font-size\">Categories &amp; Tags</h2>\n<!-- /wp:heading -->\n\n<!-- wp:paragraph {\"fontSize\":\"large\"} -->\n<p class=\"has-large-font-size\">The <strong>Taxonomy filters</strong> item in the <strong>AQL: Taxonomy</strong> panel filters by how content is organized — categories, tags, or any custom grouping — and lets you combine rules. Each card below shows its categories and tags so you can check the filter's work.</p>\n<!-- /wp:paragraph --></div>\n<!-- /wp:group -->\n\n<!-- wp:heading {\"level\":2} -->\n<h2 class=\"wp-block-heading\">Posts in a category</h2>\n<!-- /wp:heading -->\n\n<!-- wp:paragraph -->\n<p>One rule: the post is in the <em>Engineering</em> category. Because \"include children\" is on, posts in Engineering's sub-categories (like WordPress and JavaScript) count too.</p>\n<!-- /wp:paragraph -->\n\n<!-- wp:query {\"queryId\":741,\"query\":{\"perPage\":6,\"pages\":0,\"offset\":0,\"postType\":\"post\",\"order\":\"asc\",\"orderBy\":\"title\",\"author\":\"\",\"search\":\"\",\"exclude\":[],\"sticky\":\"\",\"inherit\":false,\"tax_query\":{\"relation\":\"AND\",\"queries\":[{\"id\":\"aql-tq-741a\",\"taxonomy\":\"category\",\"terms\":[\"Engineering\"],\"operator\":\"IN\",\"include_children\":true}]},\"aql_query_id\":\"taxonomy-in-category\"},\"namespace\":\"advanced-query-loop\",\"align\":\"wide\"} -->\n<div class=\"wp-block-query alignwide\"><!-- wp:post-template {\"layout\":{\"type\":\"grid\",\"columnCount\":3}} -->\n<!-- wp:group {\"backgroundColor\":\"accent-5\",\"layout\":{\"type\":\"constrained\"}} -->\n<div class=\"wp-block-group has-accent-5-background-color has-background\"><!-- wp:post-title {\"isLink\":true,\"fontSize\":\"large\"} /-->\n<!-- wp:post-terms {\"term\":\"category\",\"fontSize\":\"small\"} /-->\n<!-- wp:post-terms {\"term\":\"post_tag\",\"fontSize\":\"small\"} /--></div>\n<!-- /wp:group -->\n<!-- /wp:post-template -->\n\n<!-- wp:query-no-results -->\n<!-- wp:paragraph -->\n<p>Nothing matched.</p>\n<!-- /wp:paragraph -->\n<!-- /wp:query-no-results --></div>\n<!-- /wp:query -->\n\n<!-- wp:heading {\"level\":2} -->\n<h2 class=\"wp-block-heading\">In one group, but not another</h2>\n<!-- /wp:heading -->\n\n<!-- wp:paragraph -->\n<p>Two rules combined with AND: in the <em>Engineering</em> category, but <em>not</em> tagged \"experimental\". Compare with the list above — the experimental posts have dropped out.</p>\n<!-- /wp:paragraph -->\n\n<!-- wp:query {\"queryId\":742,\"query\":{\"perPage\":6,\"pages\":0,\"offset\":0,\"postType\":\"post\",\"order\":\"asc\",\"orderBy\":\"title\",\"author\":\"\",\"search\":\"\",\"exclude\":[],\"sticky\":\"\",\"inherit\":false,\"tax_query\":{\"relation\":\"AND\",\"queries\":[{\"id\":\"aql-tq-742a\",\"taxonomy\":\"category\",\"terms\":[\"Engineering\"],\"operator\":\"IN\",\"include_children\":true},{\"id\":\"aql-tq-742b\",\"taxonomy\":\"post_tag\",\"terms\":[\"experimental\"],\"operator\":\"NOT IN\",\"include_children\":true}]},\"aql_query_id\":\"taxonomy-not-in\"},\"namespace\":\"advanced-query-loop\",\"align\":\"wide\"} -->\n<div class=\"wp-block-query alignwide\"><!-- wp:post-template {\"layout\":{\"type\":\"grid\",\"columnCount\":3}} -->\n<!-- wp:group {\"backgroundColor\":\"accent-5\",\"layout\":{\"type\":\"constrained\"}} -->\n<div class=\"wp-block-group has-accent-5-background-color has-background\"><!-- wp:post-title {\"isLink\":true,\"fontSize\":\"large\"} /-->\n<!-- wp:post-terms {\"term\":\"category\",\"fontSize\":\"small\"} /-->\n<!-- wp:post-terms {\"term\":\"post_tag\",\"fontSize\":\"small\"} /--></div>\n<!-- /wp:group -->\n<!-- /wp:post-template -->\n\n<!-- wp:query-no-results -->\n<!-- wp:paragraph -->\n<p>Nothing matched.</p>\n<!-- /wp:paragraph -->\n<!-- /wp:query-no-results --></div>\n<!-- /wp:query -->\n\n<!-- wp:group {\"backgroundColor\":\"accent-1\",\"layout\":{\"type\":\"constrained\"}} -->\n<div class=\"wp-block-group has-accent-1-background-color has-background\"><!-- wp:heading {\"level\":3} -->\n<h3 class=\"wp-block-heading\">Try these changes</h3>\n<!-- /wp:heading -->\n\n<!-- wp:list {\"ordered\":true} -->\n<ol class=\"wp-block-list\"><!-- wp:list-item -->\n<li>Turn off \"include children\" on the first query — posts filed only under WordPress or JavaScript disappear.</li>\n<!-- /wp:list-item -->\n<!-- wp:list-item -->\n<li>Swap the first query's category from Engineering to Design.</li>\n<!-- /wp:list-item -->\n<!-- wp:list-item -->\n<li>Turn on Advanced mode for a rule to reach the full set of operators.</li>\n<!-- /wp:list-item --></ol>\n<!-- /wp:list --></div>\n<!-- /wp:group -->\n"
		},
		{
			"step": "writeFile",
			"path": "/wordpress/wp-content/plugins/advanced-query-loop-testing/guides/custom-fields.html",
			"data": "<!-- wp:group {\"align\":\"wide\",\"backgroundColor\":\"accent-5\",\"layout\":{\"type\":\"constrained\"}} -->\n<div class=\"wp-block-group alignwide has-accent-5-background-color has-background\"><!-- wp:paragraph {\"textColor\":\"accent-4\",\"fontSize\":\"small\"} -->\n<p class=\"has-accent-4-color has-text-color has-small-font-size\">AQL GUIDE · FOUNDATIONS</p>\n<!-- /wp:paragraph -->\n\n<!-- wp:heading {\"level\":2,\"fontSize\":\"xx-large\"} -->\n<h2 class=\"wp-block-heading has-xx-large-font-size\">Custom Fields</h2>\n<!-- /wp:heading -->\n\n<!-- wp:paragraph {\"fontSize\":\"large\"} -->\n<p class=\"has-large-font-size\">Custom fields are extra facts attached to a post — a rating, a price, a yes/no flag. The <strong>Meta filters</strong> item in the <strong>AQL: Meta</strong> panel turns them into filters. The recipes here carry two fields: a <em>rating</em> and a <em>featured</em> flag.</p>\n<!-- /wp:paragraph --></div>\n<!-- /wp:group -->\n\n<!-- wp:heading {\"level\":2} -->\n<h2 class=\"wp-block-heading\">One filter: only the editor's picks</h2>\n<!-- /wp:heading -->\n\n<!-- wp:paragraph -->\n<p>A single condition: the <em>featured</em> field equals \"yes\". Three recipes qualify.</p>\n<!-- /wp:paragraph -->\n\n<!-- wp:query {\"queryId\":731,\"query\":{\"perPage\":12,\"pages\":0,\"offset\":0,\"postType\":\"post\",\"order\":\"asc\",\"orderBy\":\"title\",\"author\":\"\",\"search\":\"\",\"taxQuery\":{\"category\":[%%TERM_ID:category:recipes%%]},\"exclude\":[],\"sticky\":\"\",\"inherit\":false,\"meta_query\":{\"relation\":\"AND\",\"queries\":[{\"id\":\"aql-mq-731a\",\"meta_key\":\"featured\",\"meta_value\":\"yes\",\"meta_compare\":\"=\"}]},\"aql_query_id\":\"custom-fields-single\"},\"namespace\":\"advanced-query-loop\",\"align\":\"wide\"} -->\n<div class=\"wp-block-query alignwide\"><!-- wp:post-template {\"layout\":{\"type\":\"grid\",\"columnCount\":3}} -->\n<!-- wp:group {\"backgroundColor\":\"accent-5\",\"layout\":{\"type\":\"constrained\"}} -->\n<div class=\"wp-block-group has-accent-5-background-color has-background\"><!-- wp:post-title {\"isLink\":true,\"fontSize\":\"large\"} /-->\n<!-- wp:post-excerpt {\"fontSize\":\"small\"} /--></div>\n<!-- /wp:group -->\n<!-- /wp:post-template -->\n\n<!-- wp:query-no-results -->\n<!-- wp:paragraph -->\n<p>No recipes matched.</p>\n<!-- /wp:paragraph -->\n<!-- /wp:query-no-results --></div>\n<!-- /wp:query -->\n\n<!-- wp:heading {\"level\":2} -->\n<h2 class=\"wp-block-heading\">Two filters with AND: featured and highly rated</h2>\n<!-- /wp:heading -->\n\n<!-- wp:paragraph -->\n<p>Add a second condition and choose <strong>AND</strong>: a recipe must be featured <em>and</em> rated 4 or better. The list tightens.</p>\n<!-- /wp:paragraph -->\n\n<!-- wp:query {\"queryId\":732,\"query\":{\"perPage\":12,\"pages\":0,\"offset\":0,\"postType\":\"post\",\"order\":\"asc\",\"orderBy\":\"title\",\"author\":\"\",\"search\":\"\",\"taxQuery\":{\"category\":[%%TERM_ID:category:recipes%%]},\"exclude\":[],\"sticky\":\"\",\"inherit\":false,\"meta_query\":{\"relation\":\"AND\",\"queries\":[{\"id\":\"aql-mq-732a\",\"meta_key\":\"featured\",\"meta_value\":\"yes\",\"meta_compare\":\"=\"},{\"id\":\"aql-mq-732b\",\"meta_key\":\"rating\",\"meta_value\":\"4\",\"meta_compare\":\">=\"}]},\"aql_query_id\":\"custom-fields-and\"},\"namespace\":\"advanced-query-loop\",\"align\":\"wide\"} -->\n<div class=\"wp-block-query alignwide\"><!-- wp:post-template {\"layout\":{\"type\":\"grid\",\"columnCount\":3}} -->\n<!-- wp:group {\"backgroundColor\":\"accent-5\",\"layout\":{\"type\":\"constrained\"}} -->\n<div class=\"wp-block-group has-accent-5-background-color has-background\"><!-- wp:post-title {\"isLink\":true,\"fontSize\":\"large\"} /-->\n<!-- wp:post-excerpt {\"fontSize\":\"small\"} /--></div>\n<!-- /wp:group -->\n<!-- /wp:post-template -->\n\n<!-- wp:query-no-results -->\n<!-- wp:paragraph -->\n<p>No recipes matched.</p>\n<!-- /wp:paragraph -->\n<!-- /wp:query-no-results --></div>\n<!-- /wp:query -->\n\n<!-- wp:heading {\"level\":2} -->\n<h2 class=\"wp-block-heading\">Two filters with OR: either one counts</h2>\n<!-- /wp:heading -->\n\n<!-- wp:paragraph -->\n<p>Switch the relationship to <strong>OR</strong> and a recipe only needs to pass one test: featured, <em>or</em> rated 2 and under (say, a \"loved or overlooked\" list). The list widens.</p>\n<!-- /wp:paragraph -->\n\n<!-- wp:query {\"queryId\":733,\"query\":{\"perPage\":12,\"pages\":0,\"offset\":0,\"postType\":\"post\",\"order\":\"asc\",\"orderBy\":\"title\",\"author\":\"\",\"search\":\"\",\"taxQuery\":{\"category\":[%%TERM_ID:category:recipes%%]},\"exclude\":[],\"sticky\":\"\",\"inherit\":false,\"meta_query\":{\"relation\":\"OR\",\"queries\":[{\"id\":\"aql-mq-733a\",\"meta_key\":\"featured\",\"meta_value\":\"yes\",\"meta_compare\":\"=\"},{\"id\":\"aql-mq-733b\",\"meta_key\":\"rating\",\"meta_value\":\"2\",\"meta_compare\":\"<=\"}]},\"aql_query_id\":\"custom-fields-or\"},\"namespace\":\"advanced-query-loop\",\"align\":\"wide\"} -->\n<div class=\"wp-block-query alignwide\"><!-- wp:post-template {\"layout\":{\"type\":\"grid\",\"columnCount\":3}} -->\n<!-- wp:group {\"backgroundColor\":\"accent-5\",\"layout\":{\"type\":\"constrained\"}} -->\n<div class=\"wp-block-group has-accent-5-background-color has-background\"><!-- wp:post-title {\"isLink\":true,\"fontSize\":\"large\"} /-->\n<!-- wp:post-excerpt {\"fontSize\":\"small\"} /--></div>\n<!-- /wp:group -->\n<!-- /wp:post-template -->\n\n<!-- wp:query-no-results -->\n<!-- wp:paragraph -->\n<p>No recipes matched.</p>\n<!-- /wp:paragraph -->\n<!-- /wp:query-no-results --></div>\n<!-- /wp:query -->\n\n<!-- wp:group {\"backgroundColor\":\"accent-1\",\"layout\":{\"type\":\"constrained\"}} -->\n<div class=\"wp-block-group has-accent-1-background-color has-background\"><!-- wp:heading {\"level\":3} -->\n<h3 class=\"wp-block-heading\">Try these changes</h3>\n<!-- /wp:heading -->\n\n<!-- wp:list {\"ordered\":true} -->\n<ol class=\"wp-block-list\"><!-- wp:list-item -->\n<li>Change the rating threshold from 4 to 5 in the AND query.</li>\n<!-- /wp:list-item -->\n<!-- wp:list-item -->\n<li>Flip the AND query to OR and compare the two lists.</li>\n<!-- /wp:list-item -->\n<!-- wp:list-item -->\n<li>Pair this with the Order By guide: sort the featured list by rating, highest first.</li>\n<!-- /wp:list-item --></ol>\n<!-- /wp:list --></div>\n<!-- /wp:group -->\n"
		},
		{
			"step": "writeFile",
			"path": "/wordpress/wp-content/plugins/advanced-query-loop-testing/guides/dates.html",
			"data": "<!-- wp:group {\"align\":\"wide\",\"backgroundColor\":\"accent-5\",\"layout\":{\"type\":\"constrained\"}} -->\n<div class=\"wp-block-group alignwide has-accent-5-background-color has-background\"><!-- wp:paragraph {\"textColor\":\"accent-4\",\"fontSize\":\"small\"} -->\n<p class=\"has-accent-4-color has-text-color has-small-font-size\">AQL GUIDE · FOUNDATIONS</p>\n<!-- /wp:paragraph -->\n\n<!-- wp:heading {\"level\":2,\"fontSize\":\"xx-large\"} -->\n<h2 class=\"wp-block-heading has-xx-large-font-size\">Dates</h2>\n<!-- /wp:heading -->\n\n<!-- wp:paragraph {\"fontSize\":\"large\"} -->\n<p class=\"has-large-font-size\">The <strong>AQL: Date</strong> panel filters by publish date two ways: a <strong>Dynamic range</strong> that keeps itself up to date (\"the last three months\"), or a <strong>Date relationship</strong> pinned to a specific date (\"everything before January 2025\").</p>\n<!-- /wp:paragraph --></div>\n<!-- /wp:group -->\n\n<!-- wp:heading {\"level\":2} -->\n<h2 class=\"wp-block-heading\">Dynamic range: recent posts, automatically</h2>\n<!-- /wp:heading -->\n\n<!-- wp:paragraph -->\n<p>This list shows posts from the last three months — and it will still say that next year, with no edits, because the range moves with today's date. Check the dates on the cards.</p>\n<!-- /wp:paragraph -->\n\n<!-- wp:query {\"queryId\":721,\"query\":{\"perPage\":9,\"pages\":0,\"offset\":0,\"postType\":\"post\",\"order\":\"desc\",\"orderBy\":\"date\",\"author\":\"\",\"search\":\"\",\"exclude\":[],\"sticky\":\"\",\"inherit\":false,\"date_query\":{\"range\":\"three-months\"},\"aql_query_id\":\"dates-dynamic-range\"},\"namespace\":\"advanced-query-loop\",\"align\":\"wide\"} -->\n<div class=\"wp-block-query alignwide\"><!-- wp:post-template {\"layout\":{\"type\":\"grid\",\"columnCount\":3}} -->\n<!-- wp:group {\"backgroundColor\":\"accent-5\",\"layout\":{\"type\":\"constrained\"}} -->\n<div class=\"wp-block-group has-accent-5-background-color has-background\"><!-- wp:post-title {\"isLink\":true,\"fontSize\":\"large\"} /-->\n<!-- wp:post-date {\"fontSize\":\"small\"} /--></div>\n<!-- /wp:group -->\n<!-- /wp:post-template -->\n\n<!-- wp:query-no-results -->\n<!-- wp:paragraph -->\n<p>Nothing published in the last three months.</p>\n<!-- /wp:paragraph -->\n<!-- /wp:query-no-results --></div>\n<!-- /wp:query -->\n\n<!-- wp:heading {\"level\":2} -->\n<h2 class=\"wp-block-heading\">Date relationship: before a specific date</h2>\n<!-- /wp:heading -->\n\n<!-- wp:paragraph -->\n<p>A fixed cutoff works the other way — it never moves. This list only shows posts published before January 1, 2025, which on this site means the recipe archive.</p>\n<!-- /wp:paragraph -->\n\n<!-- wp:query {\"queryId\":722,\"query\":{\"perPage\":9,\"pages\":0,\"offset\":0,\"postType\":\"post\",\"order\":\"desc\",\"orderBy\":\"date\",\"author\":\"\",\"search\":\"\",\"exclude\":[],\"sticky\":\"\",\"inherit\":false,\"date_query\":{\"relation\":\"before\",\"date_primary\":\"2025-01-01T00:00:00\",\"inclusive\":false},\"aql_query_id\":\"dates-before\"},\"namespace\":\"advanced-query-loop\",\"align\":\"wide\"} -->\n<div class=\"wp-block-query alignwide\"><!-- wp:post-template {\"layout\":{\"type\":\"grid\",\"columnCount\":3}} -->\n<!-- wp:group {\"backgroundColor\":\"accent-5\",\"layout\":{\"type\":\"constrained\"}} -->\n<div class=\"wp-block-group has-accent-5-background-color has-background\"><!-- wp:post-title {\"isLink\":true,\"fontSize\":\"large\"} /-->\n<!-- wp:post-date {\"fontSize\":\"small\"} /--></div>\n<!-- /wp:group -->\n<!-- /wp:post-template -->\n\n<!-- wp:query-no-results -->\n<!-- wp:paragraph -->\n<p>Nothing published before that date.</p>\n<!-- /wp:paragraph -->\n<!-- /wp:query-no-results --></div>\n<!-- /wp:query -->\n\n<!-- wp:group {\"backgroundColor\":\"accent-1\",\"layout\":{\"type\":\"constrained\"}} -->\n<div class=\"wp-block-group has-accent-1-background-color has-background\"><!-- wp:heading {\"level\":3} -->\n<h3 class=\"wp-block-heading\">Try these changes</h3>\n<!-- /wp:heading -->\n\n<!-- wp:list {\"ordered\":true} -->\n<ol class=\"wp-block-list\"><!-- wp:list-item -->\n<li>Switch the dynamic range to Last month and watch the list shrink.</li>\n<!-- /wp:list-item -->\n<!-- wp:list-item -->\n<li>Change the relationship from Before to After — the two lists trade places.</li>\n<!-- /wp:list-item -->\n<!-- wp:list-item -->\n<li>Try Between with two dates to build a \"from the archives\" list for one specific stretch.</li>\n<!-- /wp:list-item --></ol>\n<!-- /wp:list --></div>\n<!-- /wp:group -->\n"
		},
		{
			"step": "writeFile",
			"path": "/wordpress/wp-content/plugins/advanced-query-loop-testing/guides/order-by.html",
			"data": "<!-- wp:group {\"align\":\"wide\",\"backgroundColor\":\"accent-5\",\"layout\":{\"type\":\"constrained\"}} -->\n<div class=\"wp-block-group alignwide has-accent-5-background-color has-background\"><!-- wp:paragraph {\"textColor\":\"accent-4\",\"fontSize\":\"small\"} -->\n<p class=\"has-accent-4-color has-text-color has-small-font-size\">AQL RECIPE · FOUNDATIONS</p>\n<!-- /wp:paragraph -->\n\n<!-- wp:heading {\"level\":2,\"fontSize\":\"xx-large\"} -->\n<h2 class=\"wp-block-heading has-xx-large-font-size\">Order By</h2>\n<!-- /wp:heading -->\n\n<!-- wp:paragraph {\"fontSize\":\"large\"} -->\n<p class=\"has-large-font-size\">The <strong>AQL: Order by</strong> panel controls the order posts appear in. Start with a single property, sort by a custom field, or add a secondary sort to settle ties. Every example below queries the same recipe posts so the differences are easy to spot.</p>\n<!-- /wp:paragraph --></div>\n<!-- /wp:group -->\n\n<!-- wp:heading {\"level\":2} -->\n<h2 class=\"wp-block-heading\">Pick the order</h2>\n<!-- /wp:heading -->\n\n<!-- wp:paragraph -->\n<p>The <strong>Order</strong> item holds two settings: the property to sort by and the direction. This query orders posts by title, A to Z.</p>\n<!-- /wp:paragraph -->\n\n<!-- wp:query {\"queryId\":601,\"query\":{\"perPage\":12,\"pages\":0,\"offset\":0,\"postType\":\"post\",\"order\":\"asc\",\"orderBy\":\"title\",\"author\":\"\",\"search\":\"\",\"taxQuery\":{\"category\":[%%TERM_ID:category:recipes%%]},\"exclude\":[],\"sticky\":\"\",\"inherit\":false,\"aql_query_id\":\"order-by-title\"},\"namespace\":\"advanced-query-loop\",\"align\":\"wide\"} -->\n<div class=\"wp-block-query alignwide\"><!-- wp:post-template {\"layout\":{\"type\":\"grid\",\"columnCount\":3}} -->\n<!-- wp:group {\"backgroundColor\":\"accent-5\",\"layout\":{\"type\":\"constrained\"}} -->\n<div class=\"wp-block-group has-accent-5-background-color has-background\"><!-- wp:post-title {\"isLink\":true,\"fontSize\":\"large\"} /-->\n<!-- wp:post-date {\"fontSize\":\"small\"} /--></div>\n<!-- /wp:group -->\n<!-- /wp:post-template -->\n\n<!-- wp:query-no-results -->\n<!-- wp:paragraph -->\n<p>No recipes matched.</p>\n<!-- /wp:paragraph -->\n<!-- /wp:query-no-results --></div>\n<!-- /wp:query -->\n\n<!-- wp:heading {\"level\":2} -->\n<h2 class=\"wp-block-heading\">Sort by a custom field</h2>\n<!-- /wp:heading -->\n\n<!-- wp:paragraph -->\n<p>Choose <strong>Meta Value (Numeric)</strong> as the property and a <strong>Meta key to sort by</strong> field appears. This query sorts by a numeric <em>rating</em> field, highest first — and recipes without a rating stay in the list, at the end.</p>\n<!-- /wp:paragraph -->\n\n<!-- wp:query {\"queryId\":602,\"query\":{\"perPage\":12,\"pages\":0,\"offset\":0,\"postType\":\"post\",\"order\":\"desc\",\"orderBy\":\"meta_value_num\",\"author\":\"\",\"search\":\"\",\"taxQuery\":{\"category\":[%%TERM_ID:category:recipes%%]},\"exclude\":[],\"sticky\":\"\",\"inherit\":false,\"orderby_meta_key\":\"rating\",\"aql_query_id\":\"order-by-rating\"},\"namespace\":\"advanced-query-loop\",\"align\":\"wide\"} -->\n<div class=\"wp-block-query alignwide\"><!-- wp:post-template {\"layout\":{\"type\":\"grid\",\"columnCount\":3}} -->\n<!-- wp:group {\"backgroundColor\":\"accent-5\",\"layout\":{\"type\":\"constrained\"}} -->\n<div class=\"wp-block-group has-accent-5-background-color has-background\"><!-- wp:post-title {\"isLink\":true,\"fontSize\":\"large\"} /-->\n<!-- wp:post-excerpt {\"fontSize\":\"small\"} /--></div>\n<!-- /wp:group -->\n<!-- /wp:post-template -->\n\n<!-- wp:query-no-results -->\n<!-- wp:paragraph -->\n<p>No recipes matched.</p>\n<!-- /wp:paragraph -->\n<!-- /wp:query-no-results --></div>\n<!-- /wp:query -->\n\n<!-- wp:heading {\"level\":2} -->\n<h2 class=\"wp-block-heading\">Using a secondary sort</h2>\n<!-- /wp:heading -->\n\n<!-- wp:paragraph -->\n<p>When two posts match on the main property, a secondary sort decides who goes first. Add <strong>Secondary sort</strong> from the panel menu and pick its own property and direction. This query orders by publish date, and recipes published the same day line up alphabetically instead of at random.</p>\n<!-- /wp:paragraph -->\n\n<!-- wp:query {\"queryId\":603,\"query\":{\"perPage\":12,\"pages\":0,\"offset\":0,\"postType\":\"post\",\"order\":\"desc\",\"orderBy\":\"date\",\"author\":\"\",\"search\":\"\",\"taxQuery\":{\"category\":[%%TERM_ID:category:recipes%%]},\"exclude\":[],\"sticky\":\"\",\"inherit\":false,\"secondary_orderby\":{\"order_by\":\"title\",\"order\":\"asc\"},\"aql_query_id\":\"order-by-secondary-sort\"},\"namespace\":\"advanced-query-loop\",\"align\":\"wide\"} -->\n<div class=\"wp-block-query alignwide\"><!-- wp:post-template {\"layout\":{\"type\":\"grid\",\"columnCount\":3}} -->\n<!-- wp:group {\"backgroundColor\":\"accent-5\",\"layout\":{\"type\":\"constrained\"}} -->\n<div class=\"wp-block-group has-accent-5-background-color has-background\"><!-- wp:post-title {\"isLink\":true,\"fontSize\":\"large\"} /-->\n<!-- wp:post-date {\"fontSize\":\"small\"} /--></div>\n<!-- /wp:group -->\n<!-- /wp:post-template -->\n\n<!-- wp:query-no-results -->\n<!-- wp:paragraph -->\n<p>No recipes matched.</p>\n<!-- /wp:paragraph -->\n<!-- /wp:query-no-results --></div>\n<!-- /wp:query -->\n\n<!-- wp:heading {\"level\":2} -->\n<h2 class=\"wp-block-heading\">Both together: featured first, newest on top</h2>\n<!-- /wp:heading -->\n\n<!-- wp:paragraph -->\n<p>The two ideas combine naturally. Here the main sort is a <em>featured</em> custom field, so editor's picks rise to the top, and the secondary sort keeps each group in date order.</p>\n<!-- /wp:paragraph -->\n\n<!-- wp:query {\"queryId\":604,\"query\":{\"perPage\":12,\"pages\":0,\"offset\":0,\"postType\":\"post\",\"order\":\"desc\",\"orderBy\":\"meta_value\",\"author\":\"\",\"search\":\"\",\"taxQuery\":{\"category\":[%%TERM_ID:category:recipes%%]},\"exclude\":[],\"sticky\":\"\",\"inherit\":false,\"orderby_meta_key\":\"featured\",\"secondary_orderby\":{\"order_by\":\"date\",\"order\":\"desc\"},\"aql_query_id\":\"order-by-featured-first\"},\"namespace\":\"advanced-query-loop\",\"align\":\"wide\"} -->\n<div class=\"wp-block-query alignwide\"><!-- wp:post-template {\"layout\":{\"type\":\"grid\",\"columnCount\":3}} -->\n<!-- wp:group {\"backgroundColor\":\"accent-5\",\"layout\":{\"type\":\"constrained\"}} -->\n<div class=\"wp-block-group has-accent-5-background-color has-background\"><!-- wp:post-title {\"isLink\":true,\"fontSize\":\"large\"} /-->\n<!-- wp:post-excerpt {\"fontSize\":\"small\"} /-->\n<!-- wp:post-date {\"fontSize\":\"small\"} /--></div>\n<!-- /wp:group -->\n<!-- /wp:post-template -->\n\n<!-- wp:query-no-results -->\n<!-- wp:paragraph -->\n<p>No recipes matched.</p>\n<!-- /wp:paragraph -->\n<!-- /wp:query-no-results --></div>\n<!-- /wp:query -->\n\n<!-- wp:group {\"backgroundColor\":\"accent-1\",\"layout\":{\"type\":\"constrained\"}} -->\n<div class=\"wp-block-group has-accent-1-background-color has-background\"><!-- wp:heading {\"level\":3} -->\n<h3 class=\"wp-block-heading\">Try these changes</h3>\n<!-- /wp:heading -->\n\n<!-- wp:list {\"ordered\":true} -->\n<ol class=\"wp-block-list\"><!-- wp:list-item -->\n<li>Flip the first query's direction to descending and watch the alphabet reverse.</li>\n<!-- /wp:list-item -->\n<!-- wp:list-item -->\n<li>On the rating query, switch the direction to ascending — unrated recipes move to the front.</li>\n<!-- /wp:list-item -->\n<!-- wp:list-item -->\n<li>Remove the Secondary sort item from the panel menu on the third query and note how same-day recipes fall into an arbitrary order.</li>\n<!-- /wp:list-item -->\n<!-- wp:list-item -->\n<li>Set any main sort to Random and confirm the Secondary sort item hides itself.</li>\n<!-- /wp:list-item --></ol>\n<!-- /wp:list --></div>\n<!-- /wp:group -->\n"
		},
		{
			"step": "writeFile",
			"path": "/wordpress/wp-content/plugins/advanced-query-loop-testing/guides/performance.html",
			"data": "<!-- wp:group {\"align\":\"wide\",\"backgroundColor\":\"accent-5\",\"layout\":{\"type\":\"constrained\"}} -->\n<div class=\"wp-block-group alignwide has-accent-5-background-color has-background\"><!-- wp:paragraph {\"textColor\":\"accent-4\",\"fontSize\":\"small\"} -->\n<p class=\"has-accent-4-color has-text-color has-small-font-size\">AQL GUIDE · UNDER THE HOOD</p>\n<!-- /wp:paragraph -->\n\n<!-- wp:heading {\"level\":2,\"fontSize\":\"xx-large\"} -->\n<h2 class=\"wp-block-heading has-xx-large-font-size\">Performance</h2>\n<!-- /wp:heading -->\n\n<!-- wp:paragraph {\"fontSize\":\"large\"} -->\n<p class=\"has-large-font-size\">The <strong>AQL: Performance</strong> panel is the only one whose results you <em>shouldn't</em> see. Both switches make the page do less work behind the scenes — the lists look identical, they're just cheaper.</p>\n<!-- /wp:paragraph --></div>\n<!-- /wp:group -->\n\n<!-- wp:heading {\"level\":2} -->\n<h2 class=\"wp-block-heading\">Pagination: skip the counting</h2>\n<!-- /wp:heading -->\n\n<!-- wp:paragraph -->\n<p>To draw page numbers, WordPress has to count <em>every</em> matching post — even the ones it isn't showing. A \"top 4\" list like this one will never have page links, so the <strong>Pagination</strong> switch is off and that counting is skipped entirely.</p>\n<!-- /wp:paragraph -->\n\n<!-- wp:query {\"queryId\":751,\"query\":{\"perPage\":4,\"pages\":0,\"offset\":0,\"postType\":\"post\",\"order\":\"desc\",\"orderBy\":\"meta_value_num\",\"author\":\"\",\"search\":\"\",\"taxQuery\":{\"category\":[%%TERM_ID:category:recipes%%]},\"exclude\":[],\"sticky\":\"\",\"inherit\":false,\"orderby_meta_key\":\"rating\",\"disable_pagination\":true,\"aql_query_id\":\"performance-no-pagination\"},\"namespace\":\"advanced-query-loop\",\"align\":\"wide\"} -->\n<div class=\"wp-block-query alignwide\"><!-- wp:post-template {\"layout\":{\"type\":\"grid\",\"columnCount\":4}} -->\n<!-- wp:group {\"backgroundColor\":\"accent-5\",\"layout\":{\"type\":\"constrained\"}} -->\n<div class=\"wp-block-group has-accent-5-background-color has-background\"><!-- wp:post-title {\"isLink\":true,\"fontSize\":\"large\"} /-->\n<!-- wp:post-excerpt {\"fontSize\":\"small\"} /--></div>\n<!-- /wp:group -->\n<!-- /wp:post-template -->\n\n<!-- wp:query-no-results -->\n<!-- wp:paragraph -->\n<p>No recipes matched.</p>\n<!-- /wp:paragraph -->\n<!-- /wp:query-no-results --></div>\n<!-- /wp:query -->\n\n<!-- wp:heading {\"level\":2} -->\n<h2 class=\"wp-block-heading\">Caching: remember the answer</h2>\n<!-- /wp:heading -->\n\n<!-- wp:paragraph -->\n<p>With <strong>Caching</strong> on, the site saves this query's results and reuses them for later visitors instead of asking the database again. It looks exactly like any other list — that's the point. Use it for queries that don't change often, like this year-in-review of the lowest-rated experiments.</p>\n<!-- /wp:paragraph -->\n\n<!-- wp:query {\"queryId\":752,\"query\":{\"perPage\":4,\"pages\":0,\"offset\":0,\"postType\":\"post\",\"order\":\"asc\",\"orderBy\":\"meta_value_num\",\"author\":\"\",\"search\":\"\",\"taxQuery\":{\"category\":[%%TERM_ID:category:recipes%%]},\"exclude\":[],\"sticky\":\"\",\"inherit\":false,\"orderby_meta_key\":\"rating\",\"enable_caching\":true,\"disable_pagination\":true,\"aql_query_id\":\"performance-cached\"},\"namespace\":\"advanced-query-loop\",\"align\":\"wide\"} -->\n<div class=\"wp-block-query alignwide\"><!-- wp:post-template {\"layout\":{\"type\":\"grid\",\"columnCount\":4}} -->\n<!-- wp:group {\"backgroundColor\":\"accent-5\",\"layout\":{\"type\":\"constrained\"}} -->\n<div class=\"wp-block-group has-accent-5-background-color has-background\"><!-- wp:post-title {\"isLink\":true,\"fontSize\":\"large\"} /-->\n<!-- wp:post-excerpt {\"fontSize\":\"small\"} /--></div>\n<!-- /wp:group -->\n<!-- /wp:post-template -->\n\n<!-- wp:query-no-results -->\n<!-- wp:paragraph -->\n<p>No recipes matched.</p>\n<!-- /wp:paragraph -->\n<!-- /wp:query-no-results --></div>\n<!-- /wp:query -->\n\n<!-- wp:group {\"backgroundColor\":\"accent-1\",\"layout\":{\"type\":\"constrained\"}} -->\n<div class=\"wp-block-group has-accent-1-background-color has-background\"><!-- wp:heading {\"level\":3} -->\n<h3 class=\"wp-block-heading\">Good to know</h3>\n<!-- /wp:heading -->\n\n<!-- wp:list -->\n<ul class=\"wp-block-list\"><!-- wp:list-item -->\n<li>Leave Pagination on whenever the list actually shows page links — turning it off breaks them.</li>\n<!-- /wp:list-item -->\n<!-- wp:list-item -->\n<li>Cached results refresh on a timer, so a brand-new post may take a little while to appear in a cached list.</li>\n<!-- /wp:list-item -->\n<!-- wp:list-item -->\n<li>These switches matter most on big sites with thousands of posts; on a small site you won't feel the difference — and that's fine.</li>\n<!-- /wp:list-item --></ul>\n<!-- /wp:list --></div>\n<!-- /wp:group -->\n"
		},
		{
			"step": "writeFile",
			"path": "/wordpress/wp-content/plugins/advanced-query-loop-testing/guides/post-types.html",
			"data": "<!-- wp:group {\"align\":\"wide\",\"backgroundColor\":\"accent-5\",\"layout\":{\"type\":\"constrained\"}} -->\n<div class=\"wp-block-group alignwide has-accent-5-background-color has-background\"><!-- wp:paragraph {\"textColor\":\"accent-4\",\"fontSize\":\"small\"} -->\n<p class=\"has-accent-4-color has-text-color has-small-font-size\">AQL GUIDE · FOUNDATIONS</p>\n<!-- /wp:paragraph -->\n\n<!-- wp:heading {\"level\":2,\"fontSize\":\"xx-large\"} -->\n<h2 class=\"wp-block-heading has-xx-large-font-size\">Post Types &amp; Picking Posts</h2>\n<!-- /wp:heading -->\n\n<!-- wp:paragraph {\"fontSize\":\"large\"} -->\n<p class=\"has-large-font-size\">The <strong>AQL: Post</strong> panel decides <em>what</em> is in your list: which content types, which exact posts, which posts to leave out, and whether to show only child items.</p>\n<!-- /wp:paragraph --></div>\n<!-- /wp:group -->\n\n<!-- wp:heading {\"level\":2} -->\n<h2 class=\"wp-block-heading\">Post types: mix content in one list</h2>\n<!-- /wp:heading -->\n\n<!-- wp:paragraph -->\n<p>Normally a Query Loop shows one content type. Add more under <strong>Post types</strong> and they all appear together — here Posts, Projects, and Guides share one alphabetical list, each card labeled with its type.</p>\n<!-- /wp:paragraph -->\n\n<!-- wp:query {\"queryId\":711,\"query\":{\"perPage\":9,\"pages\":0,\"offset\":0,\"postType\":\"post\",\"multiple_posts\":[\"aql_project\",\"aql_guide\"],\"order\":\"asc\",\"orderBy\":\"title\",\"author\":\"\",\"search\":\"\",\"exclude\":[],\"sticky\":\"\",\"inherit\":false,\"aql_query_id\":\"post-types-mixed\"},\"namespace\":\"advanced-query-loop\",\"align\":\"wide\"} -->\n<div class=\"wp-block-query alignwide\"><!-- wp:post-template {\"layout\":{\"type\":\"grid\",\"columnCount\":3}} -->\n<!-- wp:group {\"backgroundColor\":\"accent-5\",\"layout\":{\"type\":\"constrained\"}} -->\n<div class=\"wp-block-group has-accent-5-background-color has-background\"><!-- wp:post-title {\"isLink\":true,\"fontSize\":\"large\"} /-->\n<!-- wp:aql-demo/post-type /--></div>\n<!-- /wp:group -->\n<!-- /wp:post-template -->\n\n<!-- wp:query-no-results -->\n<!-- wp:paragraph -->\n<p>Nothing matched.</p>\n<!-- /wp:paragraph -->\n<!-- /wp:query-no-results --></div>\n<!-- /wp:query -->\n\n<!-- wp:heading {\"level\":2} -->\n<h2 class=\"wp-block-heading\">Include posts: hand-pick the list</h2>\n<!-- /wp:heading -->\n\n<!-- wp:paragraph -->\n<p>Sometimes you don't want a rule — you want <em>these three, exactly</em>. Search and pick them under <strong>Include posts</strong> and only they appear.</p>\n<!-- /wp:paragraph -->\n\n<!-- wp:query {\"queryId\":712,\"query\":{\"perPage\":6,\"pages\":0,\"offset\":0,\"postType\":\"post\",\"order\":\"asc\",\"orderBy\":\"title\",\"author\":\"\",\"search\":\"\",\"exclude\":[],\"sticky\":\"\",\"inherit\":false,\"include_posts\":[{\"id\":%%POST_ID:apple-galette%%,\"title\":\"Apple Galette\"},{\"id\":%%POST_ID:focaccia%%,\"title\":\"Focaccia\"},{\"id\":%%POST_ID:lemon-loaf%%,\"title\":\"Lemon Loaf\"}],\"aql_query_id\":\"post-types-include\"},\"namespace\":\"advanced-query-loop\",\"align\":\"wide\"} -->\n<div class=\"wp-block-query alignwide\"><!-- wp:post-template {\"layout\":{\"type\":\"grid\",\"columnCount\":3}} -->\n<!-- wp:group {\"backgroundColor\":\"accent-5\",\"layout\":{\"type\":\"constrained\"}} -->\n<div class=\"wp-block-group has-accent-5-background-color has-background\"><!-- wp:post-title {\"isLink\":true,\"fontSize\":\"large\"} /-->\n<!-- wp:post-excerpt {\"fontSize\":\"small\"} /--></div>\n<!-- /wp:group -->\n<!-- /wp:post-template -->\n\n<!-- wp:query-no-results -->\n<!-- wp:paragraph -->\n<p>Nothing matched.</p>\n<!-- /wp:paragraph -->\n<!-- /wp:query-no-results --></div>\n<!-- /wp:query -->\n\n<!-- wp:heading {\"level\":2} -->\n<h2 class=\"wp-block-heading\">Exclude posts: leave a few out</h2>\n<!-- /wp:heading -->\n\n<!-- wp:paragraph -->\n<p>The reverse also works. This list shows every recipe <em>except</em> two picked under <strong>Exclude posts</strong> — look for the missing Cardamom Buns and Key Lime Pie.</p>\n<!-- /wp:paragraph -->\n\n<!-- wp:query {\"queryId\":713,\"query\":{\"perPage\":12,\"pages\":0,\"offset\":0,\"postType\":\"post\",\"order\":\"asc\",\"orderBy\":\"title\",\"author\":\"\",\"search\":\"\",\"taxQuery\":{\"category\":[%%TERM_ID:category:recipes%%]},\"exclude\":[],\"sticky\":\"\",\"inherit\":false,\"exclude_posts\":[{\"id\":%%POST_ID:cardamom-buns%%,\"title\":\"Cardamom Buns\"},{\"id\":%%POST_ID:key-lime-pie%%,\"title\":\"Key Lime Pie\"}],\"aql_query_id\":\"post-types-exclude\"},\"namespace\":\"advanced-query-loop\",\"align\":\"wide\"} -->\n<div class=\"wp-block-query alignwide\"><!-- wp:post-template {\"layout\":{\"type\":\"grid\",\"columnCount\":3}} -->\n<!-- wp:group {\"backgroundColor\":\"accent-5\",\"layout\":{\"type\":\"constrained\"}} -->\n<div class=\"wp-block-group has-accent-5-background-color has-background\"><!-- wp:post-title {\"isLink\":true,\"fontSize\":\"large\"} /--></div>\n<!-- /wp:group -->\n<!-- /wp:post-template -->\n\n<!-- wp:query-no-results -->\n<!-- wp:paragraph -->\n<p>Nothing matched.</p>\n<!-- /wp:paragraph -->\n<!-- /wp:query-no-results --></div>\n<!-- /wp:query -->\n\n<!-- wp:heading {\"level\":2} -->\n<h2 class=\"wp-block-heading\">Exclude current post: no déjà vu</h2>\n<!-- /wp:heading -->\n\n<!-- wp:paragraph -->\n<p>On a \"related content\" list you never want the page the visitor is already reading. The guide list below has <strong>Exclude current post</strong> switched on — notice this very guide is missing from it.</p>\n<!-- /wp:paragraph -->\n\n<!-- wp:query {\"queryId\":714,\"query\":{\"perPage\":12,\"pages\":0,\"offset\":0,\"postType\":\"aql_guide\",\"order\":\"asc\",\"orderBy\":\"title\",\"author\":\"\",\"search\":\"\",\"exclude\":[],\"sticky\":\"\",\"inherit\":false,\"exclude_current\":true,\"aql_query_id\":\"post-types-exclude-current\"},\"namespace\":\"advanced-query-loop\",\"align\":\"wide\"} -->\n<div class=\"wp-block-query alignwide\"><!-- wp:post-template {\"layout\":{\"type\":\"grid\",\"columnCount\":3}} -->\n<!-- wp:group {\"backgroundColor\":\"accent-5\",\"layout\":{\"type\":\"constrained\"}} -->\n<div class=\"wp-block-group has-accent-5-background-color has-background\"><!-- wp:post-title {\"isLink\":true,\"fontSize\":\"large\"} /--></div>\n<!-- /wp:group -->\n<!-- /wp:post-template -->\n\n<!-- wp:query-no-results -->\n<!-- wp:paragraph -->\n<p>Nothing matched.</p>\n<!-- /wp:paragraph -->\n<!-- /wp:query-no-results --></div>\n<!-- /wp:query -->\n\n<!-- wp:heading {\"level\":2} -->\n<h2 class=\"wp-block-heading\">Child items only: just the sub-pages</h2>\n<!-- /wp:heading -->\n\n<!-- wp:paragraph -->\n<p>For content organized under a parent — chapters under a handbook, sub-pages under a page — <strong>Child items only</strong> narrows the list to one family. This query shows only the guides filed under Start Here — including the one you're reading.</p>\n<!-- /wp:paragraph -->\n\n<!-- wp:query {\"queryId\":715,\"query\":{\"perPage\":12,\"pages\":0,\"offset\":0,\"postType\":\"aql_guide\",\"order\":\"asc\",\"orderBy\":\"title\",\"author\":\"\",\"search\":\"\",\"exclude\":[],\"sticky\":\"\",\"inherit\":false,\"post_parent\":%%POST_ID:start-here%%,\"aql_query_id\":\"post-types-children\"},\"namespace\":\"advanced-query-loop\",\"align\":\"wide\"} -->\n<div class=\"wp-block-query alignwide\"><!-- wp:post-template {\"layout\":{\"type\":\"grid\",\"columnCount\":3}} -->\n<!-- wp:group {\"backgroundColor\":\"accent-5\",\"layout\":{\"type\":\"constrained\"}} -->\n<div class=\"wp-block-group has-accent-5-background-color has-background\"><!-- wp:post-title {\"isLink\":true,\"fontSize\":\"large\"} /--></div>\n<!-- /wp:group -->\n<!-- /wp:post-template -->\n\n<!-- wp:query-no-results -->\n<!-- wp:paragraph -->\n<p>Nothing matched.</p>\n<!-- /wp:paragraph -->\n<!-- /wp:query-no-results --></div>\n<!-- /wp:query -->\n\n<!-- wp:group {\"backgroundColor\":\"accent-1\",\"layout\":{\"type\":\"constrained\"}} -->\n<div class=\"wp-block-group has-accent-1-background-color has-background\"><!-- wp:heading {\"level\":3} -->\n<h3 class=\"wp-block-heading\">Try these changes</h3>\n<!-- /wp:heading -->\n\n<!-- wp:list {\"ordered\":true} -->\n<ol class=\"wp-block-list\"><!-- wp:list-item -->\n<li>Remove Guides from the first query's Post types and watch the labels change.</li>\n<!-- /wp:list-item -->\n<!-- wp:list-item -->\n<li>Add a fourth recipe to the Include posts list.</li>\n<!-- /wp:list-item -->\n<!-- wp:list-item -->\n<li>Turn off Exclude current post on the guide list — this guide reappears.</li>\n<!-- /wp:list-item --></ol>\n<!-- /wp:list --></div>\n<!-- /wp:group -->\n"
		},
		{
			"step": "writeFile",
			"path": "/wordpress/wp-content/plugins/advanced-query-loop-testing/guides/query-basics.html",
			"data": "<!-- wp:group {\"align\":\"wide\",\"backgroundColor\":\"accent-5\",\"layout\":{\"type\":\"constrained\"}} -->\n<div class=\"wp-block-group alignwide has-accent-5-background-color has-background\"><!-- wp:paragraph {\"textColor\":\"accent-4\",\"fontSize\":\"small\"} -->\n<p class=\"has-accent-4-color has-text-color has-small-font-size\">AQL RECIPE · FOUNDATIONS</p>\n<!-- /wp:paragraph -->\n\n<!-- wp:heading {\"level\":2,\"fontSize\":\"xx-large\"} -->\n<h2 class=\"wp-block-heading has-xx-large-font-size\">Build a mixed-content query</h2>\n<!-- /wp:heading -->\n\n<!-- wp:paragraph {\"fontSize\":\"large\"} -->\n<p class=\"has-large-font-size\">Start with a normal Query block, choose the Advanced Query Loop variation, then layer AQL controls onto the core query settings.</p>\n<!-- /wp:paragraph --></div>\n<!-- /wp:group -->\n\n<!-- wp:heading {\"level\":2} -->\n<h2 class=\"wp-block-heading\">Posts and Projects together</h2>\n<!-- /wp:heading -->\n\n<!-- wp:paragraph -->\n<p>The query below uses Posts as its primary type, adds Projects, sorts titles in ascending order, and limits the result to six items. Select it and inspect <strong>Additional Post Types</strong> and <strong>Post Order By</strong>.</p>\n<!-- /wp:paragraph -->\n\n<!-- wp:query {\"queryId\":502,\"query\":{\"perPage\":6,\"pages\":0,\"offset\":0,\"postType\":\"post\",\"multiple_posts\":[\"aql_project\"],\"order\":\"asc\",\"orderBy\":\"title\",\"author\":\"\",\"search\":\"\",\"exclude\":[],\"sticky\":\"\",\"inherit\":false,\"aql_query_id\":\"query-basics-mixed\"},\"namespace\":\"advanced-query-loop\",\"align\":\"wide\"} -->\n<div class=\"wp-block-query alignwide\"><!-- wp:post-template {\"layout\":{\"type\":\"grid\",\"columnCount\":3}} -->\n<!-- wp:group {\"backgroundColor\":\"accent-5\",\"layout\":{\"type\":\"constrained\"}} -->\n<div class=\"wp-block-group has-accent-5-background-color has-background\"><!-- wp:post-title {\"isLink\":true,\"fontSize\":\"large\"} /-->\n<!-- wp:aql-demo/post-type /-->\n<!-- wp:post-date {\"fontSize\":\"small\"} /--></div>\n<!-- /wp:group -->\n<!-- /wp:post-template -->\n\n<!-- wp:query-no-results -->\n<!-- wp:paragraph -->\n<p>No mixed-content records matched.</p>\n<!-- /wp:paragraph -->\n<!-- /wp:query-no-results --></div>\n<!-- /wp:query -->\n\n<!-- wp:group {\"backgroundColor\":\"accent-1\",\"layout\":{\"type\":\"constrained\"}} -->\n<div class=\"wp-block-group has-accent-1-background-color has-background\"><!-- wp:heading {\"level\":3} -->\n<h3 class=\"wp-block-heading\">Try these changes</h3>\n<!-- /wp:heading -->\n\n<!-- wp:list {\"ordered\":true} -->\n<ol class=\"wp-block-list\"><!-- wp:list-item -->\n<li>Switch ascending off to reverse the title sequence.</li>\n<!-- /wp:list-item -->\n<!-- wp:list-item -->\n<li>Change Order By to Post ID and compare editor and frontend output.</li>\n<!-- /wp:list-item -->\n<!-- wp:list-item -->\n<li>Add Guides as a third post type and confirm all three REST collections appear.</li>\n<!-- /wp:list-item -->\n<!-- wp:list-item -->\n<li>Set Per Page to three, then add pagination for a core/AQL combined test.</li>\n<!-- /wp:list-item --></ol>\n<!-- /wp:list --></div>\n<!-- /wp:group -->\n"
		},
		{
			"step": "writeFile",
			"path": "/wordpress/wp-content/plugins/advanced-query-loop-testing/guides/query-id.html",
			"data": "<!-- wp:group {\"align\":\"wide\",\"backgroundColor\":\"accent-5\",\"layout\":{\"type\":\"constrained\"}} -->\n<div class=\"wp-block-group alignwide has-accent-5-background-color has-background\"><!-- wp:paragraph {\"textColor\":\"accent-4\",\"fontSize\":\"small\"} -->\n<p class=\"has-accent-4-color has-text-color has-small-font-size\">AQL GUIDE · UNDER THE HOOD</p>\n<!-- /wp:paragraph -->\n\n<!-- wp:heading {\"level\":2,\"fontSize\":\"xx-large\"} -->\n<h2 class=\"wp-block-heading has-xx-large-font-size\">Query ID</h2>\n<!-- /wp:heading -->\n\n<!-- wp:paragraph {\"fontSize\":\"large\"} -->\n<p class=\"has-large-font-size\">The <strong>Query ID</strong> field in the <strong>AQL: Advanced</strong> panel gives a query a name. It changes nothing on its own — it's a label a developer can use to find <em>this exact query</em> in code and customize it beyond what the panels offer.</p>\n<!-- /wp:paragraph --></div>\n<!-- /wp:group -->\n\n<!-- wp:heading {\"level\":2} -->\n<h2 class=\"wp-block-heading\">Name the query</h2>\n<!-- /wp:heading -->\n\n<!-- wp:paragraph -->\n<p>The list below is named <code>recipes-spotlight</code>. Select it in the editor, open <strong>AQL: Advanced</strong>, and you'll see the name in the Query ID field. Pick names the way you'd name a file — short, descriptive, no spaces.</p>\n<!-- /wp:paragraph -->\n\n<!-- wp:query {\"queryId\":761,\"query\":{\"perPage\":3,\"pages\":0,\"offset\":0,\"postType\":\"post\",\"order\":\"desc\",\"orderBy\":\"meta_value_num\",\"author\":\"\",\"search\":\"\",\"taxQuery\":{\"category\":[%%TERM_ID:category:recipes%%]},\"exclude\":[],\"sticky\":\"\",\"inherit\":false,\"orderby_meta_key\":\"rating\",\"disable_pagination\":true,\"aql_query_id\":\"recipes-spotlight\"},\"namespace\":\"advanced-query-loop\",\"align\":\"wide\"} -->\n<div class=\"wp-block-query alignwide\"><!-- wp:post-template {\"layout\":{\"type\":\"grid\",\"columnCount\":3}} -->\n<!-- wp:group {\"backgroundColor\":\"accent-5\",\"layout\":{\"type\":\"constrained\"}} -->\n<div class=\"wp-block-group has-accent-5-background-color has-background\"><!-- wp:post-title {\"isLink\":true,\"fontSize\":\"large\"} /-->\n<!-- wp:post-excerpt {\"fontSize\":\"small\"} /--></div>\n<!-- /wp:group -->\n<!-- /wp:post-template -->\n\n<!-- wp:query-no-results -->\n<!-- wp:paragraph -->\n<p>No recipes matched.</p>\n<!-- /wp:paragraph -->\n<!-- /wp:query-no-results --></div>\n<!-- /wp:query -->\n\n<!-- wp:heading {\"level\":2} -->\n<h2 class=\"wp-block-heading\">Hand the name to your developer</h2>\n<!-- /wp:heading -->\n\n<!-- wp:paragraph -->\n<p>That name is the whole hand-off. A developer can now target just this query — for example, to cap it at three results no matter what the block says:</p>\n<!-- /wp:paragraph -->\n\n<!-- wp:code -->\n<pre class=\"wp-block-code\"><code>add_filter( 'aql_query_vars', function ( $query_args, $block_query, $inherited ) {\n\tif ( 'recipes-spotlight' === ( $block_query['aql_query_id'] ?? '' ) ) {\n\t\t$query_args['posts_per_page'] = 3;\n\t}\n\treturn $query_args;\n}, 10, 3 );</code></pre>\n<!-- /wp:code -->\n\n<!-- wp:paragraph -->\n<p>Every other query on the site is untouched — that's what the name buys you.</p>\n<!-- /wp:paragraph -->\n\n<!-- wp:group {\"backgroundColor\":\"accent-1\",\"layout\":{\"type\":\"constrained\"}} -->\n<div class=\"wp-block-group has-accent-1-background-color has-background\"><!-- wp:heading {\"level\":3} -->\n<h3 class=\"wp-block-heading\">Good to know</h3>\n<!-- /wp:heading -->\n\n<!-- wp:list -->\n<ul class=\"wp-block-list\"><!-- wp:list-item -->\n<li>Names don't need to be unique, but giving two queries the same name means code targeting it affects both.</li>\n<!-- /wp:list-item -->\n<!-- wp:list-item -->\n<li>Renaming a query breaks any code targeting the old name — treat a name as a promise.</li>\n<!-- /wp:list-item -->\n<!-- wp:list-item -->\n<li>Every example query in these guides carries an ID — select any of them and peek at AQL: Advanced.</li>\n<!-- /wp:list-item --></ul>\n<!-- /wp:list --></div>\n<!-- /wp:group -->\n"
		},
		{
			"step": "writeFile",
			"path": "/wordpress/wp-content/plugins/advanced-query-loop-testing/guides/start-here.html",
			"data": "<!-- wp:group {\"align\":\"wide\",\"backgroundColor\":\"accent-5\",\"layout\":{\"type\":\"constrained\"}} -->\n<div class=\"wp-block-group alignwide has-accent-5-background-color has-background\"><!-- wp:paragraph {\"textColor\":\"accent-4\",\"fontSize\":\"small\"} -->\n<p class=\"has-accent-4-color has-text-color has-small-font-size\">AQL GUIDE · GETTING STARTED</p>\n<!-- /wp:paragraph -->\n\n<!-- wp:heading {\"level\":2,\"fontSize\":\"xx-large\"} -->\n<h2 class=\"wp-block-heading has-xx-large-font-size\">Start Here</h2>\n<!-- /wp:heading -->\n\n<!-- wp:paragraph {\"fontSize\":\"large\"} -->\n<p class=\"has-large-font-size\">Advanced Query Loop is the normal Query Loop block with extra settings. If you can add a Query Loop, you can use everything in these guides — no code required.</p>\n<!-- /wp:paragraph --></div>\n<!-- /wp:group -->\n\n<!-- wp:heading {\"level\":2} -->\n<h2 class=\"wp-block-heading\">Add the block</h2>\n<!-- /wp:heading -->\n\n<!-- wp:paragraph -->\n<p>In the editor, add a block and search for <strong>Advanced Query Loop</strong>. Already have a Query Loop on the page? Select it, open its block menu, and transform it into Advanced Query Loop — your existing settings come along.</p>\n<!-- /wp:paragraph -->\n\n<!-- wp:heading {\"level\":2} -->\n<h2 class=\"wp-block-heading\">Where the settings live</h2>\n<!-- /wp:heading -->\n\n<!-- wp:paragraph -->\n<p>With the block selected, the sidebar gains a set of panels, each starting with <strong>AQL:</strong>. Each one has its own guide:</p>\n<!-- /wp:paragraph -->\n\n<!-- wp:list -->\n<ul class=\"wp-block-list\"><!-- wp:list-item -->\n<li><strong>AQL: Post</strong> — mix content types, hand-pick or leave out posts, show child items.</li>\n<!-- /wp:list-item -->\n<!-- wp:list-item -->\n<li><strong>AQL: Order by</strong> — control the order, including ties.</li>\n<!-- /wp:list-item -->\n<!-- wp:list-item -->\n<li><strong>AQL: Date</strong> — recent posts automatically, or before/after a date.</li>\n<!-- /wp:list-item -->\n<!-- wp:list-item -->\n<li><strong>AQL: Meta</strong> — filter by custom fields.</li>\n<!-- /wp:list-item -->\n<!-- wp:list-item -->\n<li><strong>AQL: Taxonomy</strong> — filter by categories, tags, and more.</li>\n<!-- /wp:list-item -->\n<!-- wp:list-item -->\n<li><strong>AQL: Performance</strong> — pagination and caching.</li>\n<!-- /wp:list-item -->\n<!-- wp:list-item -->\n<li><strong>AQL: Advanced</strong> — name a query for a developer.</li>\n<!-- /wp:list-item --></ul>\n<!-- /wp:list -->\n\n<!-- wp:heading {\"level\":2} -->\n<h2 class=\"wp-block-heading\">Your first advanced query</h2>\n<!-- /wp:heading -->\n\n<!-- wp:paragraph -->\n<p>Here's a working Advanced Query Loop showing this site's recipes. Open this page in the editor, select the query below, and explore the AQL panels — every other guide builds on this.</p>\n<!-- /wp:paragraph -->\n\n<!-- wp:query {\"queryId\":701,\"query\":{\"perPage\":6,\"pages\":0,\"offset\":0,\"postType\":\"post\",\"order\":\"desc\",\"orderBy\":\"date\",\"author\":\"\",\"search\":\"\",\"taxQuery\":{\"category\":[%%TERM_ID:category:recipes%%]},\"exclude\":[],\"sticky\":\"\",\"inherit\":false,\"aql_query_id\":\"start-here-recipes\"},\"namespace\":\"advanced-query-loop\",\"align\":\"wide\"} -->\n<div class=\"wp-block-query alignwide\"><!-- wp:post-template {\"layout\":{\"type\":\"grid\",\"columnCount\":3}} -->\n<!-- wp:group {\"backgroundColor\":\"accent-5\",\"layout\":{\"type\":\"constrained\"}} -->\n<div class=\"wp-block-group has-accent-5-background-color has-background\"><!-- wp:post-title {\"isLink\":true,\"fontSize\":\"large\"} /-->\n<!-- wp:post-date {\"fontSize\":\"small\"} /--></div>\n<!-- /wp:group -->\n<!-- /wp:post-template -->\n\n<!-- wp:query-no-results -->\n<!-- wp:paragraph -->\n<p>No recipes matched.</p>\n<!-- /wp:paragraph -->\n<!-- /wp:query-no-results --></div>\n<!-- /wp:query -->\n\n<!-- wp:group {\"backgroundColor\":\"accent-1\",\"layout\":{\"type\":\"constrained\"}} -->\n<div class=\"wp-block-group has-accent-1-background-color has-background\"><!-- wp:heading {\"level\":3} -->\n<h3 class=\"wp-block-heading\">Try these changes</h3>\n<!-- /wp:heading -->\n\n<!-- wp:list {\"ordered\":true} -->\n<ol class=\"wp-block-list\"><!-- wp:list-item -->\n<li>Change Items per page from six to three.</li>\n<!-- /wp:list-item -->\n<!-- wp:list-item -->\n<li>Open the AQL: Order by panel and switch the order to Title, A → Z.</li>\n<!-- /wp:list-item -->\n<!-- wp:list-item -->\n<li>Head to the Post Types &amp; Picking Posts guide and keep going.</li>\n<!-- /wp:list-item --></ol>\n<!-- /wp:list --></div>\n<!-- /wp:group -->\n"
		},
		{
			"step": "activatePlugin",
			"pluginPath": "/wordpress/wp-content/plugins/advanced-query-loop-testing/plugin.php"
		},
		{
			"step": "runPHP",
			"code": "<?php require_once '/wordpress/wp-load.php'; update_option('permalink_structure','/%postname%/'); flush_rewrite_rules(); ?>"
		},
		{
			"step": "wp-cli",
			"command": "wp aql-demo seed"
		},
		{
			"step": "wp-cli",
			"command": "wp aql-demo recipes"
		},
		{
			"step": "wp-cli",
			"command": "wp aql-demo guides"
		},
		{
			"step": "setSiteOptions",
			"options": {
				"blogname": "Advanced Query Loop Demo",
				"blogdescription": "Every AQL feature, one guide at a time"
			}
		}
	]
}