Skip to content

The feedback archive is capped at 100 rows with no pagination, and the "N items" shown is the count of the truncation #660

Closed

Description

@serge-ivo

The feedback archive is silently capped at 100 rows, and the number shown to the user is the count of the truncation

Found in the D1 sweep prompted by #438. Shape 6, on a table whose migration explicitly forbids deleting anything.

Verified — server default 100, client sends no limit, no pagination anywhere

workers/api/src/routes/feedback.ts:74 — limit: Number(c.req.query("limit")) || 100,
workers/api/src/lib/feedback.ts:199 — const limit = Math.max(1, Math.min(opts.limit ?? 100, 500));
workers/api/src/lib/feedback.ts:210-212:

SELECT ${COLUMNS} FROM agent_feedback WHERE ${where.join(" AND ")} ORDER BY ts DESC LIMIT ${limit}

store/console/src/components/FeedbackList.tsx:42-47 — the only caller, sending no limit:

			const q = new URLSearchParams();
			if (instanceId) q.set("instance_id", instanceId);
			if (filter) q.set("status", filter);
			const data = await api<{ feedback?: FeedbackRow[] }>(`/v1/feedback?${q}`);
			setRows(data.feedback || []);

and :80-81 renders the truncated array as the total:

					<div className="text-xs text-muted">
						{rows.length} item{rows.length === 1 ? "" : "s"} · what you said went wrong, kept with the turn it is about

grep -cE "offset|load more|loadMore|page=" store/console/src/components/FeedbackList.tsx → 0.

Verified — the table is explicitly a permanent archive

workers/api/migrations/0122_agent_feedback.sql:

"NO RETENTION SWEEP ON THIS TABLE. Do not add one by symmetry with error_log/agent_events — the whole point is that it outlives them."

User-visible impact

ORDER BY ts DESC LIMIT 100 drops the oldest complaints. Past a hundred, the panel reads "100 items" forever and the earliest evidence — the reason the table was made permanent — is unreachable through any UI, with nothing indicating truncation. GET /v1/feedback returns count: rows.length (routes/feedback.ts:76), which is the same truncated number, so an API consumer is told the same thing.

Not claimed

I did not check whether any account has crossed 100 rows yet. The cap, the absent client limit, the absent pagination and the count-of-the-truncation are all quoted above.

Refs #438.

Activity

  1. added
    P2: correctnessReal defect, no live harm today — inert fields, miscounts, missing guards
    bugSomething isn't working
    frontendFrontend / UI work
    observabilityA displayed value the code cannot produce, or that means something other than its label
    on Aug 18, 2026
  2. serge-ivo commented on Aug 18, 2026

    @serge-ivo
    ContributorAuthor

    Audit — still LIVE, but one asserted measurement now returns a different number for an unrelated reason. Correcting it before it misleads.

    The issue asserts:

    grep -cE "offset|load more|loadMore|page=" store/console/src/components/FeedbackList.tsx → 0
    

    Re-run today it returns 2, and both are false positives — Tailwind underline-offset-2 classes at FeedbackList.tsx:195 and :209. Someone re-running this grep to check the ticket would read "2" as "pagination exists" and close it wrongly. A non-CSS form such as grep -cE "offset=|loadMore|\bpage=" … still returns 0.

    The finding itself is unchanged:

    • workers/api/src/routes/feedback.ts:74 — limit: Number(c.req.query("limit")) || 100
    • workers/api/src/lib/feedback.ts:199 — const limit = Math.max(1, Math.min(opts.limit ?? 100, 500));
    • :211 — … ORDER BY ts DESC LIMIT ${limit} with no cursor and no total
    • FeedbackList.tsx:42-47 builds its query with instance_id and status only, sends no limit, and has no way to ask for a second page.

    So the "N items" shown is still the count of the truncation.

    Leaving open, labels unchanged.

  3. serge-ivo commented on Aug 29, 2026

    @serge-ivo
    ContributorAuthor

    Fixed in fa6c95d. The feedback archive now paginates correctly:

    • GET /v1/feedback returns { count: total, has_more, feedback } where count is always the real archive total (via COUNT(*)), not the page length.
    • Page size defaults to 50 (was 100 hardcoded with no way to page past it); ?limit= and ?offset= are now accepted.
    • The console header now shows the real total; a 'Load more' button appears when has_more is true.
    • Tests pin: first page has has_more: true and correct total; second page delivers the tail with no repeats.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    P2: correctnessReal defect, no live harm today — inert fields, miscounts, missing guardsbugSomething isn't workingfrontendFrontend / UI workobservabilityA displayed value the code cannot produce, or that means something other than its label

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions