Skip to content

Surface the feedback quality tier in the composer #5430

Description

@CodeGhost21

Summary

Show the feedback composer's quality verdict while the user is writing, so a submission that will be refused is caught in the form and a thin-but-real one gets a nudge instead of silently reaching triage looking like a good report.

Problem

FeedbackSubmitForm.tsx has three outcomes today — accepted, rejected, error — and all of them arrive after the user presses submit. There is no state between "published" and "refused".

tinyhumansai/backend#1241 adds a deterministic quality gate ahead of moderation with three tiers:

  • block — placeholder bodies (test), bodies under two words or with no letters, keyboard mashes, and a repeat of the same user's own recent submission
  • warn — a one-word title, a body under 40 characters, or a bug report with no reproduction signal
  • pass — everything else

Without app-side work the user experience of that is worse, not better: a block becomes a 400 after they have written and sent the thing, and a warn is invisible — the submission is accepted and the reason the backend attached is dropped on the floor. The nudge only helps if it arrives while the text can still be changed.

This is also distinct from moderation. A quality verdict means "we could not act on this"; it must not read like "you were flagged", which is what the existing rejected state says.

Solution (optional)

Scope: app only (app/src/components/feedback/).

Live hint. Debounced POST /feedback/validate as the user types — { type, title, body }{ success: true, data: { tier, reason } }. The endpoint is deliberately cheap for this: deterministic and local, no moderation model call, nothing written, and it does not consume the daily submission limit. The one exception is the self-repeat check, which does hit the database.

On submit. POST /feedback applies the same rules, so the form is a convenience, not the enforcement point:

  • block → the request 400s with the reason as its message
  • warn → accepted, with quality: { tier, reason } on the response body
  • passquality.tier === 'pass', reason empty

States. Add a warn state to SubmitStatus (currently 'idle' | 'loading' | 'accepted' | 'rejected' | 'error'). Warn is advisory — it shows the reason and still allows submitting. Block disables submit and explains why. Neither should be styled as the moderation rejected state.

Strings go through the existing t('feedback.submit.*') namespace; the backend reasons are English-only and are a fallback, not the copy.

Acceptance criteria

  • Live quality hint — the composer calls POST /feedback/validate as the user types (debounced) and shows the returned reason for block and warn
  • Warn is advisory — a warned submission can still be sent, and the reason is visible before and after sending
  • Block prevents the round trip — submit is disabled while the draft is block, and a block that only the server catches surfaces its message rather than a generic error
  • Not styled as moderation — the quality states are visually and textually distinct from the existing rejected (moderation) state
  • Pass is silent — no hint, no layout shift, unchanged from today
  • Diff coverage ≥ 80% — the implementing PR meets the changed-lines coverage gate (Vitest + cargo-llvm-cov, enforced by .github/workflows/ci-lite.yml).

Related

  • tinyhumansai/backend#1241 — the quality gate, POST /feedback/validate, and quality on the submit response. Must deploy first: the endpoint is not in the published spec until then, so the SDK sync also follows the deploy.
  • tinyhumansai/backend#1236 — the backend feature issue this completes
  • tinyhumansai/backend#1133 — the parent feedback-to-GitHub effort, §7

Metadata

Metadata

Assignees

No one assigned

    Labels

    featureNet-new user-facing capability or product behavior.react-uiReact app work in app/src: pages, components, providers, store, and UX.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions