Skip to content

Capped future published dates when adding posts to feeds - #2161

Closed
sagzy wants to merge 1 commit into
mainfrom
cap-future-published-dates-in-feeds
Closed

Capped future published dates when adding posts to feeds#2161
sagzy wants to merge 1 commit into
mainfrom
cap-future-published-dates-in-feeds

Conversation

@sagzy

@sagzy sagzy commented Sep 3, 2026

Copy link
Copy Markdown
Contributor

Publishers control the published date of their posts: the Ghost Admin API and content imports accept arbitrary dates, and remote fediverse servers can claim any date they like. Both the main feed and the discovery feeds sort by published_at descending, so a post dated in the future pins itself to the top of every page — and sits above every pagination cursor — until real time catches up with its claimed date.

The ranking timestamp is now capped at the current time when a post is inserted into feeds and discovery_feeds.

Capping happens at the feed layer rather than on the post itself because post.publishedAt is canonical data that we re-emit in outgoing ActivityPub objects, and rewriting it would make our copy of a post disagree with its origin server. The feed tables' published_at is already a denormalised ranking column, so "when should this sort in a timeline" is exactly the semantic it owns. This mirrors how other fediverse servers handle it — Mastodon orders timelines by receipt time precisely so remote content can't choose its own rank.

Repost entries are unaffected: they already rank by the repost's own created_at, which we generate ourselves.

Note that rows inserted before this change keep their future dates, so any posts currently pinned will stay pinned until their claimed dates pass or the rows are cleaned up separately.

Publishers control the published date of their posts: the Ghost Admin
API and content imports accept arbitrary dates, and remote fediverse
servers can claim any date they like. Both the main feed and the
discovery feeds sort by published_at descending, so a post dated in
the future pins itself to the top of every page — and sits above every
pagination cursor — until real time catches up with its claimed date.

The ranking timestamp is now capped at the current time when a post is
inserted into feeds and discovery feeds. Capping happens at the feed
layer rather than on the post itself because post.publishedAt is
canonical data that we re-emit in outgoing ActivityPub objects, and
rewriting it would make our copy of a post disagree with its origin
server. The feed tables' published_at is already a denormalised
ranking column, so "when should this sort in a timeline" is exactly
the semantic it owns.
@coderabbitai

coderabbitai Bot commented Sep 3, 2026

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Team

Run ID: bb16399d-7c02-4d4e-81e0-6dbc68116e91

📥 Commits

Reviewing files that changed from the base of the PR and between acc4a97 and 367212b.

📒 Files selected for processing (2)
  • src/feed/feed.service.integration.test.ts
  • src/feed/feed.service.ts

Included review availability: Your plan provides up to 2 included reviews per hour; 1 remains after this review.


Walkthrough

The feed service adds capAtNow to replace future publication dates with the current time. Regular and discovery feed entries use the capped date for original posts. Reposts retain their creation timestamp. Integration tests cover both feed paths with publication dates one day in the future.

Estimated code review effort: 2 (Simple) | ~10 minutes

Merge Risk: ⚪ Minimal · up to 36721

Future-dated original posts are now placed in regular and discovery feeds using the current time, preventing them from remaining above pagination cursors while retaining canonical publication dates elsewhere. The changed insertion paths are covered and no merge-blocking risk remains.

Suggested reviewers: mike182uk

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description check ✅ Passed The description clearly explains the future-date capping behavior, its scope, and its effect on feed ranking and reposts.
Title check ✅ Passed The title is concise and accurately summarizes the main change: capping future published dates during feed insertion.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 1 functions across 2 files.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch cap-future-published-dates-in-feeds

Comment @coderabbitai help to get the list of available commands.

@chatgpt-codex-connector chatgpt-codex-connector Bot 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.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 367212b34c

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread src/feed/feed.service.ts
function capAtNow(date: Date): Date {
const now = new Date();

return date > now ? now : date;

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Preserve unique ordering when capping future timestamps

When more than one page of future-dated posts of the same type is ingested within a second, this maps their distinct publication dates to the same value, which feeds.published_at stores at whole-second precision. getFeedData() orders only by that column and paginates with published_at < cursor, so after the first 20 tied rows, every remaining row at the cursor timestamp is skipped permanently. This is particularly likely during concurrent content imports—the scenario motivating this change—so the feed needs a compound timestamp/ID cursor or another deterministic tie-breaker rather than a shared capped timestamp.

Useful? React with 👍 / 👎.

@sagzy sagzy closed this Sep 3, 2026
@sagzy
sagzy deleted the cap-future-published-dates-in-feeds branch September 3, 2026 10:01
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.

1 participant