Skip to content

feat(feedback): in-app bug reports and feature requests for the testing phase - #568

Merged
Deodat-Lawson merged 8 commits into
mainfrom
claude/bug-report-feature-request-ui-be3281
Oct 27, 2026
Merged

Deodat-Lawson merged 8 commits into
mainfrom
claude/bug-report-feature-request-ui-be3281

Conversation

@Deodat-Lawson

@Deodat-Lawson Deodat-Lawson commented Oct 27, 2026 •

Copy link
Copy Markdown
Owner

Summary

LaunchStack is still in testing, and testers had no in-app way to report a bug or ask for a feature. The contact page only opened a mailto: (or linked to GitHub issues), and nothing was stored. This PR adds a feedback system:

  • One dialog, two kinds. A bug can be sent with one line; a feature request needs only a title. Bugs carry the page, browser, window, theme, language and the last 10 client errors, all listed in the dialog and switchable off. Screenshots can be captured from the page (with scroll positions kept), pasted or attached, then marked up with highlight or redact boxes.
  • Feedback tab (Studio, screens across the top).
    • My reports: status and the team's replies, with a follow-up box.
    • Ideas: a shared feature board sorted by votes. While someone types a new request, matching ones are suggested so they can vote instead.
    • Triage: for operators only. Filter, open a report with its context and screenshots, set status and severity, reply, and "Copy as GitHub issue", which leaves out the reporter's name, workspace and screenshots.
  • Ways in:
    • a floating Feedback button, which each person can hide;
    • the account menu;
    • ⌘K and right-click "Report a bug" / "Request a feature";
    • Mod+Alt+B;
    • "Report this problem" on the error screen and on a failed source's card;
    • a banner on the contact page.
  • Who reads reports: the instance's operators, listed in FEEDBACK_ADMIN_EMAILS. While that is unset the whole feature stays hidden, so nobody files into a void.
    • Replies and status changes reach the reporter in the sidebar bell.
    • Optional FEEDBACK_WEBHOOK_URL (Slack or Discord) posts one line per new report: type, title and a triage link, with no body, context or images.

Related

None (new feature for the testing phase).

Checklist

  • pnpm check passes (lint + typecheck): tsc --noEmit exit 0 on the whole web app after merging main; eslint exit 0 on every touched file
  • pnpm --filter @launchstack/web test passes (see Testing)
  • Changeset added: not needed, no packages/* or pipelines/ change
  • New env vars documented in .env.example and apps/web/src/env.ts, plus docs/deployment.md, the Azure runbook and docker-compose.yml
  • UI changes exercised in a browser: not yet done by hand (see Notes)
  • Docs updated if behavior changed

Testing

  • Full web jest suite, with the DB-gated suites pointed at a local test Postgres: 587 of 588 suites, 6,658 passed / 41 skipped / 1 failed. The one failure, vantage-overview-screen.test.tsx › "offers to draft again…", is a timing flake under full-suite load: it passes 3/3 when run alone on this branch and passes on main, and this PR touches no Vantage code.
  • New tests: 83 server-side (32 unit, 39 route and inbox, 12 against Postgres) and 38 client-side.
    • Server: 401/403/404/400/413/429 paths, multipart parsing, idea auto-vote, visibility stripping for non-owners, webhook payload (Slack vs Discord, no body leaked), EXIF stripping.
    • Client: dialog send flow, technical-details-off payload, capture/attach, draft kept on close, similar-request voting, 429 copy, context capture, the error buffer, Feedback-tab paths, GitHub-issue markdown.
  • Other checks:
    • pnpm --filter @launchstack/store db:check --set=product: ok, 38 migrations.
    • The new migration is DDL only (4 tables) and applies to a fresh copy of the dev DB.
    • Service-map, Studio registry and i18n parity (en + zh-CN) tests pass.
  • API smoke against a dev server from this branch:
    • signed in as the seeded owner (triager) and as a member;
    • multipart bug with a screenshot, stored as WebP with the camera metadata gone and appVersion stamped;
    • the triage list shows the reporter; a member gets 403 on triage and PATCH;
    • a reply plus a status change gives two bell rows;
    • idea auto-vote; a vote on a bug is 400;
    • the webhook received one line per report and never the body.

Notes for reviewers

  • Not yet walked through in the browser by hand, and not yet through the independent QA pass. The UI is covered by jsdom tests and the API by the live smoke above. Please click through before merging, or say so and I'll finish that pass.
  • Operator matching is by email, without email verification. This app never verifies email (there is no mail transport), so only list emails of accounts that already exist: someone could sign up with a listed address that has no account yet.
  • Screenshots live in Postgres (bytea, re-encoded to WebP, capped at 5 MB in and 3 per report), like profile photos. Not S3, which avoids the absolute-URL storage problem.
  • Screenshot library: modern-screenshot (MIT) instead of html-to-image, because the latter captures scrolled panes from the top. It can't draw iframes or images from other origins that refuse CORS; the dialog then says to paste or attach instead.
  • Feedback is a link-only Studio tab (no picker tile). Whether an instance takes reports is an env setting, not a permission, and the Studio's gating is permission-only.
  • GIT_COMMIT_SHA is now passed into the web image by docker.yml, so reports, and /api/health, carry the real commit.
  • To turn it on in production: set FEEDBACK_ADMIN_EMAILS (and optionally FEEDBACK_WEBHOOK_URL) on the host after deploy.
  • Merged main in (it conflicted with the new Workspace tab in the Studio registry and i18n). Both sides were kept.

🤖 Generated with Claude Code


Note

Medium Risk
New authenticated APIs, Postgres blob storage for screenshots, and privacy-sensitive triage rules; feature is gated by env but touches notifications and broad UI surfaces once enabled.

Overview
Adds an in-app feedback system (bugs + feature ideas) that stays off until FEEDBACK_ADMIN_EMAILS names triagers; optional FEEDBACK_WEBHOOK_URL posts a one-line Slack/Discord alert with no sensitive content.

Backend: New Postgres tables and store for reports, votes (ideas only), replies, and WebP attachments (re-encoded, metadata stripped). REST under /api/feedback with multipart create, rate limits, and role-based visibility (bugs private to reporter/triagers; ideas public without author/context/screenshots). Triagers can patch status/severity; reporters get workspace inbox rows on reply/status change (notifications route now keeps feedback entries only for reports they filed).

Client: FeedbackProvider + reporter dialog (page context, optional technical details, screenshots via modern-screenshot), and a Feedback Studio tab (My reports / Ideas / Triage). Entry points include launcher, account menu, contact banner, failed-source and error-boundary flows, inbox bell deep links, and a feedback.report shortcut.

Ops: Documents new env vars; Docker CI passes GIT_COMMIT_SHA into the web image for /api/health and stamped report appVersion.

Reviewed by Cursor Bugbot for commit 1dec04e. Bugbot is set up for automated code reviews on this repo. Configure here.

Deodat-Lawson and others added 8 commits October 25, 2026 20:43
The shared contract for in-app feedback (bugs private to reporter and
triagers, feature requests on a shared board), plus what the browser
collects for a report: page, browser, window, theme, language and the last
ten client errors, a viewport screenshot via modern-screenshot (it keeps
each pane's scroll position, which html-to-image does not), the typed API
client, the Feedback tab's paths and 'Copy as GitHub issue' markdown that
leaves out who reported it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
One dialog for every signed-in page (employer pages and the workspace
picker), shown only when the instance names a triager. A bug needs one
line; a feature request a title, with matching requests on the board
offered to vote for instead. Screenshots: capture the page, paste or
attach, then box or black out parts before sending. What goes with the
report is listed in the dialog and technical details can be switched off.

Ways in: a floating Feedback button (hideable per person), the account
menu, Report a bug / Request a feature in Cmd+K and the right-click menu,
Mod+Alt+B, the error screen, a failed source's card and the contact page.
English and Simplified Chinese.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A Studio tab with its screens across the top: what I reported and the
team's replies, the shared board sorted by votes, and for the instance's
operators every report with status, severity, reply and Copy as GitHub
issue. Link-only (no picker tile), since whether an instance takes reports
is the operator's setting. Replies and status changes reach the reporter
in the sidebar bell; the shell dispatches the Report a bug shortcut.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
feedback_reports (kind, status, severity, reporter, the reporter's workspace
set null on delete, stored context, maintained vote count and last activity),
feedback_attachments (re-encoded screenshots kept in the row, like profile
photos), feedback_votes (one per person per idea) and feedback_replies.
Product migration 20261026001358_feedback, DDL only.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Session-only routes for filing (multipart, up to three screenshots
re-encoded to WebP without metadata), listing per scope (mine, the ideas
board, triage), reading, triaging, replying, voting and serving
attachments. Triagers are FEEDBACK_ADMIN_EMAILS; while it is empty the
feature is off and filing is refused. Visibility is in the queries (a bug is
its reporter's and the triagers'; anything else is a 404) and what each
reader sees is in present.ts (no author, context or screenshots on someone
else's idea; emails only for triagers).

A new report posts one line to FEEDBACK_WEBHOOK_URL (Slack text, Discord
content with mentions off) after it is stored, never the body. A triager's
reply or status change writes a workspace inbox row for the reporter, and
the inbox now keeps feedback rows instead of reading them as chat ids.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
FEEDBACK_ADMIN_EMAILS and FEEDBACK_WEBHOOK_URL declared in env.ts, passed
through Compose's shared allowlist, and documented in .env.example, the
deployment guide and the lean-deploy runbook. The web image now carries
GIT_COMMIT_SHA (CI passes github.sha; a local build says "unknown" as
/api/health always has), which feedback reports record as appVersion.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…in the bell

- Claim /api/feedback in the service map (instance-wide, unscoped), which
  the route-coverage test requires.
- The shared rate limiter answers in English; the feedback screens now say
  'too many' themselves, in the reader's language.
- A triager is an operator who is often not in the reporter's workspace,
  so the bell names them 'The team' rather than 'Someone'.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ture-request-ui-be3281

# Conflicts:
#	apps/web/src/app/employer/documents/_workspace/studioPaneIds.ts
#	apps/web/src/app/employer/documents/_workspace/types.ts
#	apps/web/src/i18n/messages/en/errors.json
#	apps/web/src/i18n/messages/en/workspace.json
#	apps/web/src/i18n/messages/zh-CN/errors.json
#	apps/web/src/i18n/messages/zh-CN/workspace.json
@vercel

vercel Bot commented Oct 27, 2026 •

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

1 Skipped Deployment
Project Deployment Actions Updated
pdr-ai-v2 Ignored Ignored Oct 27, 2026 5:38am UTC

@cursor

cursor Bot commented Oct 27, 2026

Copy link
Copy Markdown

Bugbot couldn't run - usage limit reached

Bugbot is counted against Cursor usage for this user or team, and this run hit a usage or spend limit.

A user or team admin can review and increase usage limits in the Cursor dashboard.

(requestId: 3edbd88b-b231-46bc-8513-fa27070e0763)

@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Oct 27, 2026 •

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review ✅ Completed 2026-10-27T05:41:43.739318Z 1dec04e PR opened
ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 1dec04e0e2

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +125 to +126
<DropdownMenuItem
onSelect={() => router.push(toolTabHref(FEEDBACK_TOOL_ID, "/mine"))}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Make report follow-up accessible without an active workspace

For a signed-in tester with no active workspace membership, the launcher allows filing feedback but “My reports” navigates to /employer/documents?feature=feedback, which src/middleware.ts:280–289 redirects back to /workspaces or the pending-approval page. The dialog’s “View report” uses the same workspace-only destination. Consequently, testers reporting joining or onboarding problems cannot read the team's responses or add follow-ups until they obtain an active membership. Provide a feedback destination that remains accessible in these session-only contexts.

Useful? React with 👍 / 👎.

setOpen(false);
feedback.open({
kind: "bug",
title: text.subject,

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Cap prefilled source-failure titles to the feedback limit

When a failed source has a long title, failureCopy.reportText() embeds the entire title in the subject, which is passed directly into the feedback draft. Document titles support 256 characters, whereas createFeedbackSchema rejects feedback titles longer than 140; for example, a 124-character filename produces a 145-character English subject. The input's maxLength does not truncate a programmatically supplied value, and Send remains enabled, so this reporting shortcut produces a rejected request unless the tester manually shortens the prefilled title. Truncate the subject to FEEDBACK_LIMITS.titleMax, retaining the full document name in the details.

Useful? React with 👍 / 👎.

@Deodat-Lawson
Deodat-Lawson merged commit dd61c33 into main Oct 27, 2026
14 checks passed
@Deodat-Lawson
Deodat-Lawson deleted the claude/bug-report-feature-request-ui-be3281 branch October 27, 2026 05:45
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.

1 participant