diff --git a/extension.php b/extension.php
index b10ef2c..8184632 100644
--- a/extension.php
+++ b/extension.php
@@ -123,7 +123,8 @@ final class BlueskyThreadsExtension extends Minz_Extension {
return $fallback;
}
- $html = $this->renderThread($data['thread'], true);
+ $rootAuthorDid = $data['thread']['post']['author']['did'] ?? null;
+ $html = $this->renderThread($data['thread'], true, $rootAuthorDid);
$this->writeCache($url, $html);
return $html;
}
@@ -221,7 +222,7 @@ final class BlueskyThreadsExtension extends Minz_Extension {
// Thread rendering
// -------------------------------------------------------------------------
- private function renderThread(array $node, bool $isRoot): string {
+ private function renderThread(array $node, bool $isRoot, ?string $rootAuthorDid = null): string {
if (!isset($node['post'])) {
return '';
}
@@ -229,13 +230,21 @@ final class BlueskyThreadsExtension extends Minz_Extension {
$html = $this->renderPost($node['post'], $isRoot);
if (!empty($node['replies'])) {
- $html .= '
';
+ $repliesHtml = '';
foreach ($node['replies'] as $reply) {
- if (isset($reply['post'])) {
- $html .= $this->renderThread($reply, false);
+ if (!isset($reply['post'])) {
+ continue;
}
+ if ($rootAuthorDid !== null && ($reply['post']['author']['did'] ?? null) !== $rootAuthorDid) {
+ continue;
+ }
+ $repliesHtml .= $this->renderThread($reply, false, $rootAuthorDid);
+ }
+ if ($repliesHtml !== '') {
+ $html .= '
';
+ $html .= $repliesHtml;
+ $html .= '
';
}
- $html .= '
';
}
return $html;
diff --git a/tests/BlueskyThreadsTest.php b/tests/BlueskyThreadsTest.php
index df77d77..9cd4648 100644
--- a/tests/BlueskyThreadsTest.php
+++ b/tests/BlueskyThreadsTest.php
@@ -174,6 +174,25 @@ class BlueskyThreadsTest extends TestCase {
$this->assertStringContainsString('View on Bluesky', $html);
}
+ /**
+ * A post with only a reply from another user (3mhypinzezo2l fixture) must
+ * render just the root post — the other-author reply is filtered out.
+ */
+ public function testOtherAuthorRepliesAreExcluded(): void {
+ $data = json_decode(file_get_contents(__DIR__ . '/fixtures/thread_3mhypinzezo2l.json'), true);
+ $thread = $data['thread'];
+ $rootAuthorDid = $thread['post']['author']['did'];
+
+ $html = $this->call('renderThread', $thread, true, $rootAuthorDid);
+
+ $this->assertSame(1, substr_count($html, 'bsky-post'),
+ 'Only the root post should be rendered; other-author reply must be excluded');
+ $this->assertSame(0, substr_count($html, 'bsky-replies'),
+ 'No replies wrapper should be rendered when all replies are from other authors');
+ $this->assertStringNotContainsString('lasagnazero.bsky.social', $html,
+ 'Reply author handle must not appear in output');
+ }
+
// -------------------------------------------------------------------------
// applyFacets — rich-text rendering
// -------------------------------------------------------------------------
diff --git a/tests/fixtures/thread_3mhypinzezo2l.json b/tests/fixtures/thread_3mhypinzezo2l.json
new file mode 100644
index 0000000..9307483
--- /dev/null
+++ b/tests/fixtures/thread_3mhypinzezo2l.json
@@ -0,0 +1,93 @@
+{
+ "thread": {
+ "$type": "app.bsky.feed.defs#threadViewPost",
+ "post": {
+ "uri": "at://did:plc:d4324t32vfi5xzydqbh2qdj3/app.bsky.feed.post/3mhypinzezo2l",
+ "cid": "bafyreialfdaqkwykiinxzy3xb6qhcenuwsaxi7d6qwzx4zfdyhprzo4s2e",
+ "author": {
+ "did": "did:plc:d4324t32vfi5xzydqbh2qdj3",
+ "handle": "hockeyviz.com",
+ "displayName": "Micah McCurdy ",
+ "avatar": "https://cdn.bsky.app/img/avatar/plain/did:plc:d4324t32vfi5xzydqbh2qdj3/bafkreiddyjdi3hty4z73vdoyxja3hj5n7izrsdmeoif2ppkqidbt4nsluq",
+ "associated": {
+ "chat": {
+ "allowIncoming": "all"
+ },
+ "activitySubscription": {
+ "allowSubscriptions": "followers"
+ }
+ },
+ "labels": [],
+ "createdAt": "2023-05-28T17:07:32.578Z"
+ },
+ "record": {
+ "$type": "app.bsky.feed.post",
+ "createdAt": "2026-03-26T23:01:26.959Z",
+ "facets": [],
+ "langs": [
+ "en"
+ ],
+ "tags": [],
+ "text": "Any good musician can rerecord somebody else's song but what really makes a cover worth doing is that little note of jealousy nestled in the blanket of admiration. It's the same with reproving somebody else's result or redoing a viz. Shoulda been me, the grudgingist yet in fact highest respect."
+ },
+ "bookmarkCount": 0,
+ "replyCount": 1,
+ "repostCount": 0,
+ "likeCount": 21,
+ "quoteCount": 0,
+ "indexedAt": "2026-03-26T23:01:27.160Z",
+ "labels": []
+ },
+ "replies": [
+ {
+ "$type": "app.bsky.feed.defs#threadViewPost",
+ "post": {
+ "uri": "at://did:plc:vfi77nom5cwpnhurnxhy7ltj/app.bsky.feed.post/3mhyw2ijjc22a",
+ "cid": "bafyreif6evbxbqcf5t3t6i4apm6pertxy4mylleet6a2d2fgzwqnz6inly",
+ "author": {
+ "did": "did:plc:vfi77nom5cwpnhurnxhy7ltj",
+ "handle": "lasagnazero.bsky.social",
+ "displayName": "L0",
+ "avatar": "https://cdn.bsky.app/img/avatar/plain/did:plc:vfi77nom5cwpnhurnxhy7ltj/bafkreigjurbghqtq3iogsqarnxldhl54zqdmi657pflwrrqwa2wisteq6i",
+ "associated": {
+ "activitySubscription": {
+ "allowSubscriptions": "followers"
+ }
+ },
+ "labels": [],
+ "createdAt": "2024-11-07T14:26:50.985Z"
+ },
+ "record": {
+ "$type": "app.bsky.feed.post",
+ "createdAt": "2026-03-27T00:58:47.713Z",
+ "langs": [
+ "en",
+ "ru"
+ ],
+ "reply": {
+ "parent": {
+ "cid": "bafyreialfdaqkwykiinxzy3xb6qhcenuwsaxi7d6qwzx4zfdyhprzo4s2e",
+ "uri": "at://did:plc:d4324t32vfi5xzydqbh2qdj3/app.bsky.feed.post/3mhypinzezo2l"
+ },
+ "root": {
+ "cid": "bafyreialfdaqkwykiinxzy3xb6qhcenuwsaxi7d6qwzx4zfdyhprzo4s2e",
+ "uri": "at://did:plc:d4324t32vfi5xzydqbh2qdj3/app.bsky.feed.post/3mhypinzezo2l"
+ }
+ },
+ "text": "Something about imitation and flattery\u2026"
+ },
+ "bookmarkCount": 0,
+ "replyCount": 0,
+ "repostCount": 0,
+ "likeCount": 0,
+ "quoteCount": 0,
+ "indexedAt": "2026-03-27T00:58:48.952Z",
+ "labels": []
+ },
+ "replies": [],
+ "threadContext": {}
+ }
+ ],
+ "threadContext": {}
+ }
+}