Skip to content

feat(cloud-agent): show recent feedback history in the feedback dialog - #6715

Open
maphew wants to merge 19 commits into
Kilo-Org:mainfrom
maphew:fix/cloud-agent-feedback-history
Open

maphew wants to merge 19 commits into
Kilo-Org:mainfrom
maphew:fix/cloud-agent-feedback-history

Conversation

@maphew

@maphew maphew commented Sep 25, 2026 •

Copy link
Copy Markdown
Contributor

Summary

Closes #6595. The Cloud Agent feedback dialog resets to blank every time it opens, so there is no way to tell whether something was already reported or what was said. This adds a user-scoped history list.

  • cloudAgentNextFeedback.list: returns the caller's own submissions, newest first, default 5 / max 20, with a deterministic tie-break. Scoped to kilo_user_id; no cross-user or admin read surface.
  • created_at is normalized to UTC ISO at the JSON boundary per packages/db/AGENTS.md ("Timestamp boundaries").
  • FeedbackDialog: fetches only while open, shows "Your recent feedback" under the form with loading / error / empty handling and live-region announcements, and invalidates the query after a successful submit so a new entry appears immediately.
  • Dialog body scrolls inside a fixed header (flex max-h-[85vh] flex-col), matching the tall-dialog pattern used elsewhere, and the auto-close timer is cleared on close/unmount.
  • feedback-history.ts: relative-time formatter (date-fns) with a deterministic now for tests.

Class vs individual

This covers the Cloud Agent feedback surface. The App Builder feedback dialog (separate router, project-scoped) has the same gap; left as a follow-up rather than widening this PR.

Verification

  • cloudAgentNextFeedback.list tests: own-rows-only, newest-first, default limit, explicit limit, 25-rows→20 cap, min/max/float rejection, orphaned kilo_user_id IS NULL rows excluded, empty list, ISO output shape.
  • feedback-history unit tests.
  • All test suites run under both the host TZ (UTC-7) and TZ=Asia/Kolkata (+05:30) to check timestamp handling.
  • oxlint clean on changed files, oxfmt --list-different clean, tsgo --noEmit (apps/web) passes.

Review

Two adversarial review passes were run before finalizing. Accepted findings (dialog scroll pattern, cache-vs-error precedence, cap assertion, copy, orphaned-row coverage) are applied. One reported major — that toIsoTimestamp corrupts non-UTC timestamps because the pg driver returns a Date — was disproven here: the repo's documented production shape (packages/db/AGENTS.md:62) is the timestamptz text value, and the ISO assertion passes under TZ=Asia/Kolkata. The normalization follows the documented boundary guidance.

Visual changes

The dialog gains a muted "Your recent feedback" section under the send button, each item clamped to two lines with a relative timestamp.

@maphew
maphew marked this pull request as ready for review September 25, 2026 05:34
@kilo-code-bot

kilo-code-bot Bot commented Sep 25, 2026 •

Copy link
Copy Markdown
Contributor

Code Review Summary

Status: No Issues Found | Recommendation: Merge

Executive Summary

Incremental review at HEAD a4ced4e3c: the only new commit since 76ce90f5 adds an offset check in toIsoTimestamp (cloud-agent-next-feedback-router.ts:32), which correctly rejects ambiguous offset-less timestamp text while still accepting the documented timestamptz shape (2026-04-29 01:16:12.945+00 → +00:00) and ISO values with Z or a numeric offset; existing list tests use those accepted shapes.

Assumptions: [INFERENCE] Tests were not executed (read-only mode). The incremental range also contains an upstream main merge; changes that are already on the base branch were excluded from scope, leaving the one-line guard as the only new PR-side change.

Files Reviewed (1 changed since previous review)
  • apps/web/src/routers/cloud-agent-next-feedback-router.ts (incremental change only; the other four PR files are unchanged since the prior review)
Previous Review Summaries (9 snapshots, latest commit 76ce90f)

Current summary above is authoritative. Previous snapshots are kept for context only.

Previous review (commit 76ce90f)

Status: No Issues Found | Recommendation: Merge

Executive Summary

Incremental review at HEAD 76ce90f5: the change since 012388a8 is the merge of upstream main plus a test-file split, where the new cloud-agent-next-feedback-router.list.test.ts is byte-identical to the previously reviewed list test, so the user-scoped list query, its timestamp normalization, and the dialog's history UI (with timer cleanup on close and unmount) remain correct.

Assumptions: [INFERENCE] The diff from 012388a8 to HEAD includes an upstream merge of ~4,300 files, so the review was scoped to the five PR-owned files at HEAD; the unrelated upstream changes in this branch are already on main. Tests were not executed (read-only mode); the new test file matches apps/web/jest.config.ts testMatch (**/src/**/*.test.ts). PR discussion notes an overlap with #6773; merge ordering is a coordination concern, not a code defect.

Files Reviewed (5 files)
  • apps/web/src/components/cloud-agent-next/FeedbackDialog.tsx
  • apps/web/src/components/cloud-agent-next/feedback-history.ts
  • apps/web/src/components/cloud-agent-next/feedback-history.test.ts
  • apps/web/src/routers/cloud-agent-next-feedback-router.ts
  • apps/web/src/routers/cloud-agent-next-feedback-router.list.test.ts

Previous review (commit 012388a)

Status: No Issues Found | Recommendation: Merge

Executive Summary

Incremental review at HEAD 012388a8e: the only change since the previously reviewed state is the two import-path corrections in cloud-agent-next-feedback-router.test.ts, which resolve to existing exports via the shared @kilocode/web-shared/* paths. The cloudAgentNextFeedback.list query is scoped to ctx.user.id under an authenticated baseProcedure, timestamp normalization follows packages/db/AGENTS.md, and the dialog's auto-close timer is cleared on both close and unmount. No new defects found.

Files Reviewed (5 files)
  • apps/web/src/components/cloud-agent-next/FeedbackDialog.tsx
  • apps/web/src/components/cloud-agent-next/feedback-history.ts
  • apps/web/src/components/cloud-agent-next/feedback-history.test.ts
  • apps/web/src/routers/cloud-agent-next-feedback-router.ts
  • apps/web/src/routers/cloud-agent-next-feedback-router.test.ts

The incremental diff could not be reconstructed against the previous SHA, so a full-PR review was performed; the remaining files were unchanged and previously verified.

Previous review (commit 731abc7)

Status: No Issues Found | Recommendation: Merge

Executive Summary

Incremental review at HEAD 731abc763: the only change since the previously reviewed state is two import-path corrections in cloud-agent-next-feedback-router.test.ts, which now resolve insertTestUser and db through the shared @kilocode/web-shared/* paths used by other tests in the repo. Both paths exist and export the imported symbols; no new defects introduced.

Files Reviewed (1 changed file)
  • apps/web/src/routers/cloud-agent-next-feedback-router.test.ts

Incremental diff vs. prior reviewed state is the two import lines above; the remaining PR files were unchanged and previously verified.

Previous review (commit 8a1bf88)

Status: No Issues Found | Recommendation: Merge

Executive Summary

Incremental review at HEAD 8a1bf882: the PR's apps/web files are byte-identical to the previously reviewed state at 95d377bf (the branch was rebased onto a newer main, not rewritten), so the incremental diff for the PR scope is empty and no new defects were found. The user-scoped cloudAgentNextFeedback.list query, its timestamptz-to-UTC-ISO normalization, the relative-time formatter, and the feedback-history dialog behavior (fixed header with scrolling body, cleanup of the auto-close timer, cached-data-over-error precedence) remain correct.

Files Reviewed (5 files)
  • apps/web/src/components/cloud-agent-next/FeedbackDialog.tsx
  • apps/web/src/components/cloud-agent-next/feedback-history.ts
  • apps/web/src/components/cloud-agent-next/feedback-history.test.ts
  • apps/web/src/routers/cloud-agent-next-feedback-router.ts
  • apps/web/src/routers/cloud-agent-next-feedback-router.test.ts

Previous review (commit 95d377b)

Status: No Issues Found | Recommendation: Merge

Executive Summary

Full review at commit 95d377bf: the user-scoped cloudAgentNextFeedback.list query, its timestamptz to UTC-ISO normalization per packages/db/AGENTS.md, the client-side relative-timestamp helper, and the feedback-history dialog UI (fixed header with scrolling body, live regions, and cleanup of the auto-close timer) are correct. No high-confidence defects found.

Files Reviewed (5 files)
  • apps/web/src/components/cloud-agent-next/FeedbackDialog.tsx
  • apps/web/src/components/cloud-agent-next/feedback-history.ts
  • apps/web/src/components/cloud-agent-next/feedback-history.test.ts
  • apps/web/src/routers/cloud-agent-next-feedback-router.ts
  • apps/web/src/routers/cloud-agent-next-feedback-router.test.ts

Previous review (commit 9743a82)

Status: No Issues Found | Recommendation: Merge

Executive Summary

Full re-review at commit 9743a82e (the incremental base was unreachable, so the complete PR was reviewed): the user-scoped cloudAgentNextFeedback.list query, its timestamptz→UTC-ISO normalization, the relative-timestamp helper, and the feedback-history dialog UI are all correct. No high-confidence defects found.

Files Reviewed (5 files)
  • apps/web/src/components/cloud-agent-next/FeedbackDialog.tsx
  • apps/web/src/components/cloud-agent-next/feedback-history.ts
  • apps/web/src/components/cloud-agent-next/feedback-history.test.ts
  • apps/web/src/routers/cloud-agent-next-feedback-router.ts
  • apps/web/src/routers/cloud-agent-next-feedback-router.test.ts

Previous review (commit ca2a618)

Status: No Issues Found | Recommendation: Merge

Executive Summary

Re-reviewed the full PR (5 files) at commit ca2a618a: the user-scoped cloudAgentNextFeedback.list query, its timestamptz→UTC-ISO normalization, the client relative-timestamp helper, and the feedback-history dialog UI. The query is scoped to ctx.user.id, the limit input is bounded (1-20), the auto-close timer is cleared on dialog change and unmount, and the changed tests assert the claimed behavior. No high-confidence defects found.

Files Reviewed (5 files)
  • apps/web/src/components/cloud-agent-next/FeedbackDialog.tsx
  • apps/web/src/components/cloud-agent-next/feedback-history.ts
  • apps/web/src/components/cloud-agent-next/feedback-history.test.ts
  • apps/web/src/routers/cloud-agent-next-feedback-router.ts
  • apps/web/src/routers/cloud-agent-next-feedback-router.test.ts

Previous review (commit 6e67c74)

Status: No Issues Found | Recommendation: Merge

Executive Summary

Incremental review of commit 6e67c745 (feedback-history scroll layout, history loading/error ordering, and the max-limit router test) found no new high-confidence defects.

Files Reviewed (3 files)
  • apps/web/src/components/cloud-agent-next/FeedbackDialog.tsx
  • apps/web/src/components/cloud-agent-next/feedback-history.ts
  • apps/web/src/routers/cloud-agent-next-feedback-router.test.ts

Previous review (commit 7ea2528)

Status: No Issues Found | Recommendation: Merge

Reviewed the new user-scoped cloudAgentNextFeedback.list query, its timestamptz-to-ISO normalization, the client-side relative-timestamp helper, and the feedback dialog history UI. The query is correctly scoped to ctx.user.id, the limit input is bounded (1–20), the auto-close timer is cleared on unmount and on dialog state change, and the changed test files assert the claimed behavior. No high-confidence defects found.

Files Reviewed (5 files)
  • apps/web/src/components/cloud-agent-next/FeedbackDialog.tsx
  • apps/web/src/components/cloud-agent-next/feedback-history.ts
  • apps/web/src/components/cloud-agent-next/feedback-history.test.ts
  • apps/web/src/routers/cloud-agent-next-feedback-router.ts
  • apps/web/src/routers/cloud-agent-next-feedback-router.test.ts

Reviewed by deepseek-flash-latest · Input: 38.1K · Output: 10K · Cached: 670.2K

Review guidance: REVIEW.md from base branch main

maphew and others added 5 commits October 13, 2026 14:57
The feedback dialog forgot every submission once it closed. Add a
user-scoped `list` query to cloudAgentNextFeedback and render the caller's
five most recent submissions under the form, so people can see they already
reported something and what they said.

Closes Kilo-Org#6595
Clear auto-close timer on dialog change and unmount. Add live
regions for submit and history states. Show load and error states
for history. Return ISO timestamps from list. Order by id on ties.
Trim list fields to id, text, created_at.

Co-authored-by: kiloconnect[bot] <240665456+kiloconnect[bot]@users.noreply.github.com>
- Cap the dialog at 85vh with scroll so a full history list cannot clip the
  send button on short viewports.
- Test that orphaned rows whose owner was deleted (`kilo_user_id IS NULL`)
  are never returned.
- Apply oxfmt to the routes/helper touched by the previous commit.
- Keep the dialog header and close button fixed; scroll only the body
  (`flex max-h-[85vh] flex-col` + inner `overflow-y-auto`), matching the
  tall-dialog pattern used elsewhere.
- Drop the nested history scroller now that the body scrolls.
- Prefer cached history over a transient background-refetch error.
- Make the history error copy say how to retry.
- Assert the 20-row cap with 25 rows instead of an empty table.
@maphew
maphew force-pushed the fix/cloud-agent-feedback-history branch from 731abc7 to 012388a Compare October 13, 2026 21:57
…dback-history

# Conflicts:
#	apps/web/src/routers/cloud-agent-next-feedback-router.test.ts

@maphew maphew left a comment

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Sweep: branch is up to date and the review is clean. Note this overlaps with No.6773, which now carries the same Cloud Agent feedback history after app-builder was removed upstream; only one should merge.

@gil-kilo gil-kilo left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Reviewed the full diff against main. Server-side scoping (ctx.user.id), input bounds, and ISO normalization all look right. Four inline notes attached: one on the offset handling in the timestamp normalizer (the same bug exists on both sides of the boundary), one on the success view hiding the refreshed history, one on the duplicated normalizer, and one on the default-limit test not actually pinning the default.

if (!value) return null;
const iso = value.includes('T')
? value
: value.replace(' ', 'T').replace(/([+-]\d{2})$/, '$1:00');

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Offset normalization only covers a bare ±HH.

The rewrite only handles +00, but the comment above promises we accept the driver's text shape generally. Two shapes break:

  • 2026-01-01T00:00:00+0530 (compact offset, emitted by Postgres/several serializers) fails the [+-]\d{2}:?\d{2}$ check only if the : is required — here it passes — but 2026-01-01 00:00:00+0530 is rewritten by the ([+-]\d{2})$ branch only when it ends in exactly two digits after the sign, so the no-T compact form survives to new Date(), which V8 parses as local time; the instant shifts.
  • 2026-01-01 00:00:00+05 is rewritten to +0:00 — not a valid offset — and new Date() returns Invalid Date, silently dropping the timestamp.

Normalize every offset shape explicitly before parsing:

function toIsoTimestamp(value: string | null | undefined): string | null {
  if (!value) return null;
  const normalized = value
    .replace(' ', 'T')
    .replace(/([+-]\d{2})(\d{2})$/, '$1:$2')
    .replace(/([+-]\d{2})$/, '$1:00');
  if (!/(?:[zZ]|[+-]\d{2}:\d{2})$/.test(normalized)) return null;
  const time = new Date(normalized).getTime();
  return Number.isNaN(time) ? null : new Date(time).toISOString();
}

The identical helper in apps/web/src/components/cloud-agent-next/feedback-history.ts:22-25 has the same two bugs — worth fixing both from one shared function (see the note on that file).


<div className="space-y-4">
<div className="min-h-0 flex-1 space-y-4 overflow-y-auto">
{showSuccess ? (

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

The refreshed history is never visible on success.

onSuccess sets showSuccess, which replaces the whole body — including the new history block — and then closes the dialog after 1200 ms. handleOpenChange already refetches on reopen because the query is enabled: isOpen, so the invalidateQueries(...) added in onSuccess cannot deliver the stated "a new entry appears immediately" behavior: by the time the history could re-render, it is unmounted, and by the time the dialog reopens the fetch happens regardless.

Either keep the form and history mounted and show the success message inline (e.g. above the send button) so the invalidated list actually renders the new entry:

{showSuccess && (
  <div className="flex items-center gap-2" role="status" aria-live="polite">
    <Check className="h-4 w-4 text-green-500" />
    <span className="text-sm text-green-500">Thank you for your feedback!</span>
  </div>
)}
<Textarea ... />

or, if the full-screen success state is intentional, drop the invalidateQueries call so the code does not imply a behavior the UI does not provide.

// rejected rather than parsed as local time.
const iso = value.includes('T')
? value
: value.replace(' ', 'T').replace(/([+-]\d{2})$/, '$1:00');

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Duplicated normalization with a divergent regex.

This is a copy of toIsoTimestamp in apps/web/src/routers/cloud-agent-next-feedback-router.ts:27-34, but the accept-check differs (/[zZ]|[+-]\d{2}:?\d{2}$/ here vs /(?:[zZ]|[+-]\d{2}:?\d{2})$/ there). Any fix to the offset handling has to be made twice, and a shape accepted by one side can be rejected by the other.

Extract one parseDbTimestamp(value: string | null | undefined): Date | null next to the shared timestamp boundary and have both callers use it, e.g.:

export function parseDbTimestamp(value: string | null | undefined): Date | null {
  if (!value) return null;
  const normalized = value
    .replace(' ', 'T')
    .replace(/([+-]\d{2})(\d{2})$/, '$1:$2')
    .replace(/([+-]\d{2})$/, '$1:00');
  if (!/(?:[zZ]|[+-]\d{2}:\d{2})$/.test(normalized)) return null;
  const date = new Date(normalized);
  return Number.isNaN(date.getTime()) ? null : date;
}

The router then does parseDbTimestamp(row.created_at)?.toISOString() ?? null and this module does formatDistance(parseDbTimestamp(value)!, now, { addSuffix: true }).

const caller = await createCallerForUser(regularUser.id);
const result = await caller.cloudAgentNextFeedback.list();

expect(result.map(row => row.feedback_text)).toEqual([

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

This does not pin the default limit.

Six rows are inserted and five are asserted, so the test passes for any DEFAULT_FEEDBACK_HISTORY_LIMIT from 5 to 20. Raising the default to 10 keeps the suite green, and the separate cap test only pins the maximum. Assert the length explicitly:

expect(result).toHaveLength(5);
expect(result.map(row => row.feedback_text)).toEqual([
  'submission 5',
  'submission 4',
  'submission 3',
  'submission 2',
  'submission 1',
]);

or compare a default call against list({ limit: 5 }) for equality.

This branch has not been deployed

No deployments
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.

[Feedback] Cloud Agent feedback has no history of past submissions and no way to follow up

2 participants