Skip to content

A repost by somebody you follow reaches the feed - #381

Merged
sepehr-safari merged 1 commit into
mainfrom
the-repost-row
Sep 22, 2026
Merged

sepehr-safari merged 1 commit into
mainfrom
the-repost-row

Conversation

@sepehr-safari

Copy link
Copy Markdown
Contributor

Closes #258.

Plaza published reposts and counted other people's, and could never show one.

The card is the note, not the wrapper

A repost becomes the note it points at: its id, its author, its words. Everything else falls out, because the rest of the file already keys on event_id.

two follows repost one note one row
a repost of something already shown not drawn twice
the counts under the row the reposted note's

Amethyst gates the wrapper entire reaction bar off (NoteCompose.kt:774) to reach the same place. All four reference clients key dedup on the reposted note rather than the wrapper; Amethyst is distinctBy { replyTo.last().idHex } with the comment "only the most recent repost per feed".

The embedded copy is a shortcut, not a source

The target is resolved from the store by the e tag, never rendered out of the wrapper content. Every reference client does this, for the same reason: the embedded copy is whatever the reposter pasted, and no relay serving it vouches for it. Coracle spells out the consequence, that a forged embedded copy costs a round trip rather than rendering.

It still earns its keep. plazaIngest hands it to store.ingest, which checks its signature like any other event, so the note is usually in hand without asking a relay. A forged copy is not stored, the feed finds nothing to draw, and the row is skipped, which is what Notedeck does for a target it cannot resolve.

A repost forces the full read

The splice path keys on the arriving event own id and merges by its own timestamp. For a repost neither is the card: the note it points at may already be on screen under its own name, with a different id and time. Teaching the splice to unwrap means teaching it all three, and the full read already knows how. Reposts are a small share of arrivals, so a rebuild on one beats a splice that puts the wrong row in the wrong place.

A bug this found in what merged this morning

unsupportedKindChip from #377 asks for an icon named "file". ui.appIcon resolves an app-registered name, this app registers ten, and file is not one, so it draws the missing-icon fallback: a slashed circle. No compile error, no failing test, nothing in CI. It is dashed-ring now, which is what this app already uses wherever there is nothing to draw.

The repost byline carries no icon for the same reason: there is no repost glyph registered, and a word is honest until there is one.

Tests

647 to 651. Checked as load bearing by making the resolution return the wrapper instead of the note, types left valid so it was a behaviour change rather than a compile error: two of three fail, and the third tests a path that returns before reaching it. My first attempt at that check was a compile error, which proves nothing, so I redid it.

Plaza published reposts and counted other people's, and could never show one. Nothing asked relays for a kind 6 by author, two more checks dropped one if it arrived anyway, and the model had no way to carry it.

The card a repost becomes IS the note it points at: its id, its author, its words. Everything else then falls out, because the rest of this file already keys on `event_id`. Dedup collapses two follows passing one note into a single row and refuses to draw a note the window already holds. Engagement is already counted against the note's own id, so the numbers under the row belong to what was reposted rather than to the wrapper. Amethyst gates the wrapper's entire reaction bar off to arrive at the same place.

The target is resolved from the store by the `e` tag, never rendered out of the wrapper's content. All four reference clients do it this way and the reason is the same in each: the embedded copy is whatever the reposter pasted, and no relay serving it vouches for it. The copy still earns its keep one door up, where `plazaIngest` hands it to `store.ingest` like any other event and has its signature checked there. A forged copy is simply not stored, the feed finds nothing to draw, and the row is skipped.

A repost forces the fuller read rather than splicing. The splice path is keyed on the arriving event's own id and merges it by its own timestamp, and for a repost neither is the card: the note it points at may already be on screen under its own name, with a different id and a different time. Teaching the splice to unwrap means teaching it all three. Reposts are a small share of arrivals, so a rebuild on one is cheaper than a splice that puts the wrong row in the wrong place.

Dedup is a hash keyed on the render handle and compared on the full id, for the reason `heldIndex` gives one screen up. A look-back scan would have been quadratic in how far the reader has paged, because the feed grows as they read and is never handed back.

## A bug this found in what shipped this morning

`unsupportedKindChip` asked for an icon named "file". `ui.appIcon` resolves an APP-REGISTERED name, this app registers ten, and "file" is not among them, so an unknown name draws the missing-icon fallback: a slashed circle. No compile error, no failing test, and nothing in CI to catch it. It is `dashed-ring` now, which is what this app already uses wherever there is nothing to draw.

The repost byline carries no icon for the same reason. There is no repost glyph registered, and a word is the honest choice until there is one.

Tests go to 651. Checked as load bearing by making the resolution return the wrapper instead of the note, with the types left valid so it was a behaviour change and not a compile error: two of the three fail, and the third tests a path that returns before reaching it. My first attempt at that check was a compile error, which proves nothing.

Closes #258.
@sepehr-safari
sepehr-safari merged commit 8b0b239 into main Sep 22, 2026
6 checks passed
@sepehr-safari
sepehr-safari deleted the the-repost-row branch September 22, 2026 14:39
@sepehr-safari sepehr-safari mentioned this pull request Sep 25, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Reposts by the people I follow never reach the feed

1 participant