diff --git a/app/src/agentworld/pages/FeedSection.test.tsx b/app/src/agentworld/pages/FeedSection.test.tsx
index 10f2d3124..b9352efa7 100644
--- a/app/src/agentworld/pages/FeedSection.test.tsx
+++ b/app/src/agentworld/pages/FeedSection.test.tsx
@@ -171,6 +171,44 @@ describe('Feed list', () => {
expect(screen.getByText(/3 comments/i)).toBeInTheDocument();
});
+ test('sorts populated feed items newest first', async () => {
+ const olderItem = {
+ ...sampleFeedItem,
+ post: {
+ ...samplePost,
+ postId: 'post-old',
+ body: 'Old update',
+ createdAt: '2026-06-01T12:00:00Z',
+ },
+ };
+ const newerItem = {
+ ...sampleFeedItem,
+ post: {
+ ...samplePost,
+ postId: 'post-new',
+ body: 'Newest update',
+ createdAt: '2026-06-02T12:00:00Z',
+ },
+ };
+
+ vi.mocked(apiClient.graphql.homeFeed).mockResolvedValue({
+ items: [olderItem, newerItem],
+ count: 2,
+ });
+
+ render();
+
+ await waitFor(() => {
+ expect(screen.getByText('Newest update')).toBeInTheDocument();
+ expect(screen.getByText('Old update')).toBeInTheDocument();
+ });
+
+ expect(
+ screen.getByText('Newest update').compareDocumentPosition(screen.getByText('Old update')) &
+ Node.DOCUMENT_POSITION_FOLLOWING
+ ).toBeTruthy();
+ });
+
test('shows wallet-locked error when wallet is not configured', async () => {
vi.mocked(apiClient.graphql.homeFeed).mockRejectedValue(new Error('wallet is not configured'));
render();
diff --git a/app/src/agentworld/pages/FeedSection.tsx b/app/src/agentworld/pages/FeedSection.tsx
index 6f087e3fd..aea13e19e 100644
--- a/app/src/agentworld/pages/FeedSection.tsx
+++ b/app/src/agentworld/pages/FeedSection.tsx
@@ -15,6 +15,7 @@
* Pattern mirrors ExploreSection / MarketplaceSection: useState + useEffect
* fetch, PanelScaffold wrapper, StatusBlock for loading/error/empty states.
*/
+import debug from 'debug';
import { useCallback, useEffect, useRef, useState } from 'react';
import PanelScaffold from '../../components/layout/PanelScaffold';
@@ -28,6 +29,8 @@ import {
import { fetchWalletStatus } from '../../services/walletApi';
import { apiClient } from '../AgentWorldShell';
+const log = debug('agentworld:feed');
+
// ── State types ───────────────────────────────────────────────────────────────
type FeedState =
@@ -57,6 +60,28 @@ function isWalletLocked(message: string): boolean {
);
}
+function postCreatedAtMillis(item: GqlHomeFeedItem): number {
+ const millis = Date.parse(item.post.createdAt);
+ return Number.isFinite(millis) ? millis : 0;
+}
+
+function sortedHomeFeedItems(result: { items?: GqlHomeFeedItem[] } | null | undefined) {
+ const items = Array.isArray(result?.items) ? [...result.items] : [];
+ const originalOrder = items.map(item => item.post.postId).join('\0');
+
+ items.sort((left, right) => postCreatedAtMillis(right) - postCreatedAtMillis(left));
+
+ if (items.length > 1 && originalOrder !== items.map(item => item.post.postId).join('\0')) {
+ log('sorted home feed newest-first', {
+ count: items.length,
+ newestCreatedAt: items[0]?.post.createdAt,
+ oldestCreatedAt: items.at(-1)?.post.createdAt,
+ });
+ }
+
+ return items;
+}
+
/** Centered status message for loading / error / info states. */
function StatusBlock({ tone, title, body }: { tone: string; title: string; body?: string }) {
return (
@@ -550,7 +575,7 @@ export default function FeedSection() {
.homeFeed({ limit: 50 })
.then(result => {
if (cancelled) return;
- const items = Array.isArray(result?.items) ? result.items : [];
+ const items = sortedHomeFeedItems(result);
setFeedState({ status: 'ok', items });
})
.catch((err: unknown) => {
@@ -624,7 +649,7 @@ export default function FeedSection() {
.deletePost(post.postId)
.then(() => {
void apiClient.graphql.homeFeed({ limit: 50 }).then(result => {
- const items = Array.isArray(result?.items) ? result.items : [];
+ const items = sortedHomeFeedItems(result);
setFeedState({ status: 'ok', items });
});
})
@@ -635,7 +660,7 @@ export default function FeedSection() {
const refetchFeed = () => {
void apiClient.graphql.homeFeed({ limit: 50 }).then(result => {
- const items = Array.isArray(result?.items) ? result.items : [];
+ const items = sortedHomeFeedItems(result);
setFeedState({ status: 'ok', items });
});
};