diff --git a/mateclaw-server/src/main/java/vip/mate/activity/ActivityFeedController.java b/mateclaw-server/src/main/java/vip/mate/activity/ActivityFeedController.java
index 132b1183..59c0f5ff 100644
--- a/mateclaw-server/src/main/java/vip/mate/activity/ActivityFeedController.java
+++ b/mateclaw-server/src/main/java/vip/mate/activity/ActivityFeedController.java
@@ -10,6 +10,7 @@ import org.springframework.web.bind.annotation.*;
import vip.mate.approval.model.ToolApprovalEntity;
import vip.mate.approval.repository.ToolApprovalMapper;
import vip.mate.audit.model.AuditEventEntity;
+import vip.mate.audit.repository.AuditEventMapper;
import vip.mate.audit.service.AuditEventService;
import vip.mate.common.result.R;
@@ -49,8 +50,28 @@ import java.util.Map;
public class ActivityFeedController {
private final AuditEventService auditEventService;
+ private final AuditEventMapper auditEventMapper;
private final ToolApprovalMapper toolApprovalMapper;
+ /**
+ * RFC-090 §4.5 — paginated activity feed.
+ *
+ *
Pagination strategy:
+ *
+ * - Single-source filter (source=audit | approval) →
+ * direct {@code BaseMapper.selectPage(...)} on the matching
+ * table. Both total and records are SQL-accurate.
+ * - Combined feed (source unset) → fetch
+ * {@code page*size} rows from each side, merge by time-desc,
+ * slice to the requested window. {@code total} is the sum
+ * of {@code selectCount} across both tables — exact for
+ * count, best-effort for time-merge ordering at very deep
+ * page numbers (the merge buffer is bounded but typical
+ * use stays within a few hundred rows).
+ *
+ *
+ * Caps: {@code size} clamped to [1, 200]; {@code page} ≥ 1.
+ */
@Operation(summary = "Unified activity feed (audit + approval + tool calls)")
@GetMapping("/feed")
public R