Skip to content

Do not prepend a mention the reply already carries - #3680

Open
pfefferle wants to merge 3 commits into
trunkfrom
fix/reply-mention-duplicate
Open

Do not prepend a mention the reply already carries#3680
pfefferle wants to merge 3 commits into
trunkfrom
fix/reply-mention-duplicate

Conversation

@pfefferle

Copy link
Copy Markdown
Member

Fixes #3646

Proposed changes:

Transformer\Comment::get_content() prepends a mention link for every actor in the reply chain, and it does so unconditionally: it never checks whether the content already mentions that actor. A reply written by hand in WordPress never does, so nobody noticed. A reply composed through the ActivityPub API usually does, because the client writes the mention itself, the way Mastodon does. So the federated object carries it twice.

The reporter's own object shows it. Fetched from mediaformat.org, the content has two rel="mention" links to the same actor: ours first, with the title="@user@host" attribute our template adds, then the client's, without it. The tag array had the actor once, so only the content prefix was wrong.

The fix skips the prefix for an actor the content already mentions, matched the same way the tag side keys mentions: a link whose href is the actor's URL, or the bare @user@host handle. The regular WordPress reply, where the author typed no mention, is unchanged.

I read the report as a fix for the visible symptom rather than a decision on whether the server should add a mention to API replies at all. That larger question is open, and worth its own issue if we want to go the way Mastodon does and leave the content entirely to the client.

One test fix rode along: the class added its WebFinger mock in wpSetUpBeforeClass, but WP_UnitTestCase snapshots the hook table around every test, so that filter was only as durable as what an earlier class left behind, and the existing reply-context test passed by ordering luck. The mock now lives in set_up() / tear_down(), which is why the new test could be made to pass in the full suite at all.

Other information:

  • Have you written new tests for your changes, if applicable?

Testing instructions:

test_content_does_not_duplicate_an_existing_mention covers three cases: mention as a link, mention as a handle, and no mention. The first two fail against trunk, the third passes on both, so the regular path is provably untouched.

  • git show origin/trunk:includes/transformer/class-comment.php > includes/transformer/class-comment.php
  • wp-env run tests-cli --env-cwd="wp-content/plugins/activitypub" vendor/bin/phpunit --filter content_does_not_duplicate, see the two failures.
  • git checkout -- includes/transformer/class-comment.php, all three pass.

By hand: reply to a remote reply from an ActivityPub client and type the mention yourself. Before, the federated note shows the person twice. After, once.

Changelog entry

In the branch as .github/changelog/fix-reply-mention-duplicate, patch / fixed.

Copilot AI lite review requested due to automatic review settings August 28, 2026 13:15
@pfefferle pfefferle self-assigned this Aug 28, 2026
@pfefferle
pfefferle requested a review from a team August 28, 2026 13:15

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Pull request overview

Fixes duplicate reply-context mentions in federated comment content by skipping auto-prepended mentions when the author already included them (common for C2S/API-composed replies), and stabilizes/extends PHPUnit coverage for this behavior.

Changes:

  • Skip prepending a reply-context mention when the comment content already mentions the actor (by handle or link).
  • Move the WebFinger HTTP mock to per-test setup/teardown and add a data-driven test for mention de-duplication.
  • Add a Changelogger entry describing the user-visible fix.

Reviewed changes

Copilot reviewed 3 out of 3 changed files in this pull request and generated 2 comments.

File Description
includes/transformer/class-comment.php Adds content_mentions() gating to avoid duplicating reply-context mention links in outgoing content.
tests/phpunit/tests/includes/transformer/class-test-comment.php Makes the WebFinger mock deterministic per test and adds coverage for existing-mention scenarios.
.github/changelog/fix-reply-mention-duplicate Documents the fix for release notes (patch/fixed).
Suppressed comments (1)

tests/phpunit/tests/includes/transformer/class-test-comment.php:162

  • If the parent actor URL includes a trailing slash, the auto-prepended mention href will include it as well. Update the expectation for the "no mention" case so it continues to validate the output while covering the trailing-slash variant.
			'no mention'          => array(
				'thanks',
				'<p><a rel="mention" class="u-url mention" href="https://remote.example/@author" title="@author@remote.example">@author</a> thanks</p>',
			),

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment thread includes/transformer/class-comment.php
Comment thread tests/phpunit/tests/includes/transformer/class-test-comment.php

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 3 out of 3 changed files in this pull request and generated 1 comment.

Suppressed comments (2)

tests/phpunit/tests/includes/transformer/class-test-comment.php:54

  • tear_down() also calls a WordPress global function without a leading backslash in a namespaced file. Prefix it with \ for consistency with the codebase's namespaced WordPress function style.
	public function tear_down() {
		remove_filter( 'pre_http_request', array( self::class, 'pre_http_request' ) );

		parent::tear_down();
	}

tests/phpunit/tests/includes/transformer/class-test-comment.php:141

  • In this namespaced test file, the new code calls get_comment() without a leading backslash. Use \get_comment() to follow the project's namespaced WordPress function convention.
		$object = ( new Comment( get_comment( $test_comment_id ) ) )->to_object();

		$this->assertSame( $expected, $object->get_content() );

Comment on lines +41 to 45
public function set_up() {
parent::set_up();

// Mock the WebFinger wp_safe_remote_get.
add_filter( 'pre_http_request', array( self::class, 'pre_http_request' ), 10, 3 );
}
@mediaformat

Copy link
Copy Markdown
Contributor

Hi @pfefferle thanks for engaging with this.

I read the report as a fix for the visible symptom rather than a decision on whether the server should add a mention to API replies at all.

Having given it some more thought, I think committing to the ActivityPub API would translate to bypassing the auto-mention of every actor in the reply chain, letting the client and user ultimately handle actor mentions.

@pfefferle

Copy link
Copy Markdown
Member Author

I think you are right, and that is how Mastodon treats client content: the client sent the mentions it meant, the server should not add to them.

The reason this PR stops at removing the duplicate is that it cannot tell a client-authored reply apart from one typed into the WordPress comment form. A reply from the API is stored with a user_id and nothing else, the same as a logged-in comment; the protocol and source_id meta only exist for comments that arrived from a remote server. So there is nothing for the transformer to check yet.

The fix for that is to mark comments created through the API at the point they are stored, and then skip the reply context for those. That is a storage change and a behaviour decision, so I opened it as its own issue rather than growing this one: #3683. WordPress-native replies would keep the auto-mention, since a comment form has no way to type one. This PR stays as the safety net for comments that already exist.

@mediaformat

Copy link
Copy Markdown
Contributor

I figured it was a temporary measure, thanks for confirming!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

C2S API: Reply includes automatic mention & manual mention

3 participants