diff --git a/tests/phpunit/tests/html-api/wpHtmlTagProcessor.php b/tests/phpunit/tests/html-api/wpHtmlTagProcessor.php
index 22ace3890f..da2fcd1e5c 100644
--- a/tests/phpunit/tests/html-api/wpHtmlTagProcessor.php
+++ b/tests/phpunit/tests/html-api/wpHtmlTagProcessor.php
@@ -460,6 +460,136 @@ class Tests_HtmlApi_WpHtmlTagProcessor extends WP_UnitTestCase {
 		);
 	}
 
+	/**
+	 * Ensures that an attribute added via set_attribute() is reported by
+	 * get_attribute_names_with_prefix() without first flushing the updates
+	 * through get_updated_html().
+	 *
+	 * @ticket 64567
+	 *
+	 * @covers WP_HTML_Tag_Processor::get_attribute_names_with_prefix
+	 */
+	public function test_get_attribute_names_with_prefix_reflects_set_attribute_without_flushing() {
+		$processor = new WP_HTML_Tag_Processor( '<div data-foo="bar">Test</div>' );
+		$processor->next_tag();
+		$processor->set_attribute( 'data-test-id', '14' );
+
+		$this->assertSame(
+			array( 'data-foo', 'data-test-id' ),
+			$processor->get_attribute_names_with_prefix( 'data-' ),
+			'Enqueued attribute added via set_attribute() was not reflected before flushing updates.'
+		);
+	}
+
+	/**
+	 * Ensures that an attribute removed via remove_attribute() is no longer
+	 * reported by get_attribute_names_with_prefix() without first flushing the
+	 * updates through get_updated_html().
+	 *
+	 * @ticket 64567
+	 *
+	 * @covers WP_HTML_Tag_Processor::get_attribute_names_with_prefix
+	 */
+	public function test_get_attribute_names_with_prefix_reflects_remove_attribute_without_flushing() {
+		$processor = new WP_HTML_Tag_Processor( '<div data-foo="bar" data-keep="1">Test</div>' );
+		$processor->next_tag();
+		$processor->remove_attribute( 'data-foo' );
+
+		$this->assertSame(
+			array( 'data-keep' ),
+			$processor->get_attribute_names_with_prefix( 'data-' ),
+			'Enqueued attribute removal via remove_attribute() was not reflected before flushing updates.'
+		);
+	}
+
+	/**
+	 * Ensures that a class enqueued via add_class() surfaces the `class`
+	 * attribute in get_attribute_names_with_prefix() without first flushing the
+	 * updates through get_updated_html().
+	 *
+	 * @ticket 64567
+	 *
+	 * @covers WP_HTML_Tag_Processor::get_attribute_names_with_prefix
+	 */
+	public function test_get_attribute_names_with_prefix_reflects_added_class_without_flushing() {
+		$processor = new WP_HTML_Tag_Processor( '<div data-foo="bar">Test</div>' );
+		$processor->next_tag();
+		$processor->add_class( 'highlight' );
+
+		$this->assertSame(
+			array( 'class' ),
+			$processor->get_attribute_names_with_prefix( 'class' ),
+			'Enqueued class attribute created via add_class() was not reflected before flushing updates.'
+		);
+	}
+
+	/**
+	 * Ensures get_attribute_names_with_prefix() agrees with get_attribute()
+	 * after pending updates, returning each name once with no stale entries.
+	 *
+	 * @ticket 64567
+	 *
+	 * @covers WP_HTML_Tag_Processor::get_attribute_names_with_prefix
+	 */
+	public function test_get_attribute_names_with_prefix_agrees_with_get_attribute_after_updates() {
+		$processor = new WP_HTML_Tag_Processor( '<div data-a="1" data-b="2" data-c="3">Test</div>' );
+		$processor->next_tag();
+		$processor->set_attribute( 'data-b', 'updated' ); // Change an existing attribute.
+		$processor->remove_attribute( 'data-c' );         // Remove an existing attribute.
+		$processor->set_attribute( 'data-d', 'new' );     // Add a new attribute.
+
+		$names = $processor->get_attribute_names_with_prefix( 'data-' );
+		sort( $names );
+
+		$this->assertSame(
+			array( 'data-a', 'data-b', 'data-d' ),
+			$names,
+			'Reported attribute names did not reflect the enqueued changes before flushing updates.'
+		);
+
+		foreach ( $names as $name ) {
+			$this->assertNotNull(
+				$processor->get_attribute( $name ),
+				"get_attribute_names_with_prefix() reported '{$name}' but get_attribute() reports it as absent."
+			);
+		}
+
+		$this->assertNull(
+			$processor->get_attribute( 'data-c' ),
+			'Removed attribute data-c should be null via get_attribute().'
+		);
+		$this->assertNotContains(
+			'data-c',
+			$names,
+			'Removed attribute data-c should not appear in get_attribute_names_with_prefix().'
+		);
+	}
+
+	/**
+	 * Ensures the internal 'modifiable text' lexical update enqueued by
+	 * set_modifiable_text() does not leak into get_attribute_names_with_prefix()
+	 * as if it were an attribute name.
+	 *
+	 * The update is stored in the same lexical_updates array as attribute
+	 * changes but under the string key 'modifiable text', so a naive skip that
+	 * only ignores integer keys would report it for any matching prefix.
+	 *
+	 * @ticket 64567
+	 *
+	 * @covers WP_HTML_Tag_Processor::get_attribute_names_with_prefix
+	 */
+	public function test_get_attribute_names_with_prefix_ignores_modifiable_text_update() {
+		$processor = new WP_HTML_Tag_Processor( '<script id="x">old</script>' );
+		$processor->next_tag();
+		$processor->set_modifiable_text( 'new content' );
+
+		$this->assertSame(
+			array( 'id' ),
+			$processor->get_attribute_names_with_prefix( '' ),
+			"The internal 'modifiable text' update key leaked as an attribute name."
+		);
+	}
+
 	/**
 	 * @ticket 56299
 	 *
