Skip to content

feat(web): add batched feedback submission over tRPC and HTTP with a per-minute limit - #6864

Merged
iscekic merged 2 commits into
mainfrom
kwf/owner-kilo-mcp-feedback-20260929
Sep 29, 2026
Merged

iscekic merged 2 commits into
mainfrom
kwf/owner-kilo-mcp-feedback-20260929

Conversation

@iscekic

@iscekic iscekic commented Sep 29, 2026

Copy link
Copy Markdown
Collaborator

Changelog for users

  • Authenticated agents submit feedback or bug reports through the feedback.submit mutation and the HTTP POST /feedback endpoint.
  • Unauthenticated callers are refused with 401 from the endpoint and an unauthorized error from the mutation.
  • A second submission within a minute returns too-many-requests with a message stating one per minute and asking the agent to batch.
  • The existing userFeedback.create mutation now shares the same storage and the same one-per-minute limit.
  • MCP initialize instructions and every tool-call error point agents at the feedback URL and ask them to batch.

Changelog for maintainers

  • The owner's "d2" store maps to the existing user_feedback PostgreSQL table; this app has no D1 or R2 binding.
  • Both entry points funnel into one submission service that owns the limit, the insert, and the Slack notification.
  • The limit reads the user's most recent row and skips an unparseable timestamp instead of blocking all future submissions.
  • The catalog gains feedback.submit; the checked-in skill is generated from its template, so regenerate both together.
  • The feedback pointer URL is built from the deploy web base URL, so dev and prod point at different apps.
  • Existing feedback writes gain a new refusal path; check any caller that submits repeatedly.

E2E proof

[MCP] initialize instructions, tool-call errors, and the catalog point agents at the batched feedback endpoint

Asserted value: KWF_MCP feedback pointer present=true. Sense check (jev): probability 0.95

Base log: backend-assert 41a25bc7941d exited 1
$ set -euo pipefail
$ cd services/kilo-mcp
$ pnpm exec vitest run src/kwf-feedback-pointer.test.ts
KWF_MCP feedback pointer present=false catalog=false instructions=false error=false
   → expected 'This server exposes the Kilo API thro…' to contain 'https://app.kilo.ai/feedback'
 Test Files  1 failed (1)
      Tests  1 failed (1)
⎯⎯⎯⎯⎯⎯⎯ Failed Tests 1 ⎯⎯⎯⎯⎯⎯⎯
 FAIL  |unit| src/kwf-feedback-pointer.test.ts > Kilo MCP feedback pointer > names the feedback endpoint in initialize instructions, tool errors, and the catalog
AssertionError: expected 'This server exposes the Kilo API thro…' to contain 'https://app.kilo.ai/feedback'
Expected: "https://app.kilo.ai/feedback"
Received: "This server exposes the Kilo API through two tools: search (find catalog endpoints) and call (invoke one by path). Search before every call. Each result carries a kind: "query" reads data,…
 ❯ src/kwf-feedback-pointer.test.ts:57:26
     55|       `KWF_MCP feedback pointer present=${instructionsOk && errorOk &&…
     56|     );
     57|     expect(instructions).toContain('https://app.kilo.ai/feedback');
       |                          ^
     58|     expect(instructions.toLowerCase()).toContain('batch');
     59|     expect(errJson.error.data?.['path']).toBe('does.not.exist');
⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[1/1]⎯
Head log: backend-assert 41a25bc7941d exited 0
$ set -euo pipefail
$ cd services/kilo-mcp
$ pnpm exec vitest run src/kwf-feedback-pointer.test.ts
KWF_MCP feedback pointer present=true catalog=true instructions=true error=true
 ✓ |unit| src/kwf-feedback-pointer.test.ts > Kilo MCP feedback pointer > names the feedback endpoint in initialize instructions, tool errors, and the catalog 25ms
 Test Files  1 passed (1)
      Tests  1 passed (1)
   Start at  04:53:43
   Duration  1.13s (transform 455ms, setup 0ms, import 982ms, tests 26ms, environment 0ms)
stderr | src/kwf-feedback-pointer.test.ts
CIMD (Client ID Metadata Document) is disabled: add '"compatibility_flags": ["global_fetch_strictly_public"]' to your wrangler.jsonc to enable. See: https://developers.cloudflare.com/workers/configur…

[tRPC] feedback.submit stores one submission and refuses a second within a minute

Asserted value: KWF_FEEDBACK rate-limit code=TOO_MANY_REQUESTS rows=1 router_present=true. Sense check (jev): probability 0.92

Base log: backend-assert 2b5062d148e8 exited 1
$ set -euo pipefail
$ cd apps/web
$ set -a
$ . ./.env.test
$ set +a
$ NODE_ENV=test pnpm exec jest --config kwf-feedback.jest.config.cjs --runInBand --verbose src/lib/feedback/kwf-feedback-submit.test.ts
    KWF_FEEDBACK rate-limit code=undefined rows=0 router_present=false first_id= load_err=Configuration error:
FAIL src/lib/feedback/kwf-feedback-submit.test.ts
      69 |   expect(mockFeedbackStore.rows[0].feedback_text).toBe(`kwf-${NS}-first`);
      70 |   expect(mockFeedbackStore.rows[0].kilo_user_id).toBe(user.id);
      at Object.toBeNull (src/lib/feedback/kwf-feedback-submit.test.ts:67:30)
Test Suites: 1 failed, 1 total
Tests:       1 failed, 1 total
Snapshots:   0 total
Time:        0.154 s
Ran all test suites matching src/lib/feedback/kwf-feedback-submit.test.ts.
Head log: backend-assert 2b5062d148e8 exited 0
$ set -euo pipefail
$ cd apps/web
$ set -a
$ . ./.env.test
$ set +a
$ NODE_ENV=test pnpm exec jest --config kwf-feedback.jest.config.cjs --runInBand --verbose src/lib/feedback/kwf-feedback-submit.test.ts
    KWF_FEEDBACK rate-limit code=TOO_MANY_REQUESTS rows=1 router_present=true first_id=row-1 load_err=
      at Object.log (src/lib/feedback/kwf-feedback-submit.test.ts:66:11)
PASS src/lib/feedback/kwf-feedback-submit.test.ts
  ✓ stores one submission and refuses a second within a minute (934 ms)
Test Suites: 1 passed, 1 total
Tests:       1 passed, 1 total
Snapshots:   0 total
Time:        1.084 s
Ran all test suites matching src/lib/feedback/kwf-feedback-submit.test.ts.

[HTTP] POST /feedback refuses unauthenticated callers, stores a submission, and returns 429 with the batching message

Asserted value: KWF_FEEDBACK_HTTP rate-limit first=200 second=429 unauth=401 rows=1 route_present=true. Sense check (jev): probability 0.94

Base log: backend-assert 25c87e172f8a exited 1
$ set -euo pipefail
$ cd apps/web
$ set -a
$ . ./.env.test
$ set +a
$ NODE_ENV=test pnpm exec jest --config kwf-feedback.jest.config.cjs --runInBand --verbose src/app/feedback/kwf-feedback-route.test.ts
    KWF_FEEDBACK_HTTP rate-limit first=undefined second=undefined unauth=undefined rows=0 route_present=false first_id= load_err=Configuration error:
FAIL src/app/feedback/kwf-feedback-route.test.ts
      88 |   expect(firstStatus).toBe(200);
      89 |   expect(mockFeedbackStore.rows.length).toBe(1);
      at Object.toBeNull (src/app/feedback/kwf-feedback-route.test.ts:86:20)
Test Suites: 1 failed, 1 total
Tests:       1 failed, 1 total
Snapshots:   0 total
Time:        0.159 s
Ran all test suites matching src/app/feedback/kwf-feedback-route.test.ts.
Head log: backend-assert 25c87e172f8a exited 0
$ set -euo pipefail
$ cd apps/web
$ set -a
$ . ./.env.test
$ set +a
$ NODE_ENV=test pnpm exec jest --config kwf-feedback.jest.config.cjs --runInBand --verbose src/app/feedback/kwf-feedback-route.test.ts
    KWF_FEEDBACK_HTTP rate-limit first=200 second=429 unauth=401 rows=1 route_present=true first_id=row-1 load_err=
      at Object.log (src/app/feedback/kwf-feedback-route.test.ts:85:11)
PASS src/app/feedback/kwf-feedback-route.test.ts
  ✓ refuses unauthenticated callers, stores one, and 429s the second with the batching message (359 ms)
Test Suites: 1 passed, 1 total
Tests:       1 passed, 1 total
Snapshots:   0 total
Time:        0.49 s
Ran all test suites matching src/app/feedback/kwf-feedback-route.test.ts.
Owner request

Kilo-Org/cloud repo, Kilo MCP server: expose two new entry points that do the same thing: a tRPC method feedback and an HTTP /feedback endpoint. Both let an authenticated agent submit feedback and bug reports about the Kilo platform. Unauthenticated calls are refused. Store each submission in "d2" for now (the owner wrote "d2"; use the storage the repo already uses that matches it, most likely Cloudflare D1 or R2, and name the choice in the PR). Rate limit: 1 submission per minute per authenticated agent/user; a limited call returns a clear error that says the limit is 1 per minute and asks the agent to batch its feedback into one submission. Update the Kilo MCP skill/instructions so agents are steered to report feedback via this endpoint and to batch it; MCP error responses also point agents at the feedback endpoint. Proof: tests for auth refusal, a stored submission via tRPC and via /feedback, and the second call within a minute refused with the batching message.

@iscekic iscekic self-assigned this Sep 29, 2026
@iscekic
iscekic marked this pull request as ready for review September 29, 2026 06:21
Comment thread apps/web/src/lib/feedback/submit.ts Outdated
@kilo-code-bot

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

Copy link
Copy Markdown
Contributor

Code Review Summary

Status: No Issues Found | Recommendation: Merge

Executive Summary

The follow-up commit makes the feedback rate-limit read and insert atomic by wrapping both in one transaction under a per-user transaction-scoped pg_advisory_xact_lock; the change is correct and the previously reported concurrency gap is resolved.

Files Reviewed (1 file)
  • apps/web/src/lib/feedback/submit.ts
Previous Review Summary (commit 32e84e8)

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

Previous review (commit 32e84e8)

Status: 1 Issues Found | Recommendation: Address before merge

Overview

Severity Count
CRITICAL 0
WARNING 1
SUGGESTION 0
Issue Details (click to expand)

WARNING

File Line Issue
apps/web/src/lib/feedback/submit.ts 143 Rate-limit check and insert are separate queries, so concurrent submissions can both pass and exceed the advertised one-per-minute limit
Files Reviewed (9 files)
  • .kilo/skills/kilo-mcp/SKILL.md - 0 issues
  • apps/web/src/app/feedback/route.ts - 0 issues
  • apps/web/src/lib/feedback/submit.ts - 1 issue
  • apps/web/src/routers/feedback-router.ts - 0 issues
  • apps/web/src/routers/root-router.ts - 0 issues
  • apps/web/src/routers/user-feedback-router.ts - 0 issues
  • apps/web/src/scripts/mcp-catalog/skill-template.md - 0 issues
  • services/kilo-mcp/catalog.json - 0 issues
  • services/kilo-mcp/src/index.ts - 0 issues

Fix these issues in Kilo Cloud


Reviewed by deepseek-v4.1-flash · Input: 0 · Output: 0 · Cached: 0

Review guidance: REVIEW.md from base branch main

@iscekic
iscekic marked this pull request as draft September 29, 2026 06:54
@iscekic
iscekic marked this pull request as ready for review September 29, 2026 15:27
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants