feat: Phase 8 polish -- feedback endpoint, prompt tuning, and action bar#135
feat: Phase 8 polish -- feedback endpoint, prompt tuning, and action bar#135
Conversation
- Table-driven TestChatFeedback covering auth, validation, slog logging - TestChatFeedback_TruncatesLongQuestion verifies server-side truncation - Custom captureLogHandler for slog assertion
…and slog logging - ChatFeedback handler validates auth, rating (up/down/none), message_index - Server-side truncation of question to 100 chars (defense in depth) - Logs feedback via slog.Info with username, rating, message_index, question - Route registered at POST /api/v1/chat/feedback with "write" rate limit
…ripMarkdown - stripMarkdown utility: headers, bold, italic, links, images, code, blockquotes, lists, tables, edge cases - Action bar: visibility on assistant vs user messages, streaming exclusion, hover classes - Copy button: strips markdown, writes to clipboard, checkmark feedback - Feedback buttons: thumbs up/down toggle, aria-pressed, sendFeedback integration - ChatMessages feedback integration: toggle logic, truncated question, sendFeedback calls
…tion - TestSystemPromptContainsUrgencyTiers checks for 70/80/90% thresholds, expedite, burst capacity - TestSystemPromptContainsBudgetJustification checks for deployment failure, SLA, developer velocity - TestSystemPromptNoCalendarReferences ensures no Q1-Q4 or fiscal year references
… justification - Added urgency tier guidance mapped to 70/80/90% utilization thresholds - Added budget justification section: deployment failure risk, SLA exposure, developer velocity impact - Uses relative timing throughout, no calendar-specific references - System prompt stays under 10,000 character budget - All existing and new tests pass
…ant messages - stripMarkdown utility: regex-based Markdown-to-plain-text conversion - sendFeedback API function: fire-and-forget POST to /api/v1/chat/feedback - CopyButton: strips markdown, writes to clipboard, checkmark icon for 2s - FeedbackButtons: thumbs up/down toggle with green/red active states - Action bar: hover-reveal on desktop (md:group-hover), always visible on mobile - Feedback state managed in ChatMessages, keyed by message index - Resets feedback state when conversation is cleared
- SUMMARY.md with task commits, decisions, self-check - STATE.md updated: phase 8 in progress, plan 1 of 2 complete - ROADMAP.md updated with phase 08 progress - REQUIREMENTS.md: POL-02 and POL-03 marked complete
- 08-02-SUMMARY.md created with task commits, decisions, and self-check - STATE.md updated to 100% complete, all 17 plans done - ROADMAP.md phase 8 plans marked complete - REQUIREMENTS.md POL-01 marked complete
PR Review: Phase 8 Polish -- Feedback Endpoint, Prompt Tuning, and Action BarOverall this is a well-structured, well-tested PR. The feature decomposition is clean, the test coverage is solid, and the fire-and-forget feedback design is a sensible call for non-critical telemetry. A few items worth addressing before merge: Issues1.
|
There was a problem hiding this comment.
Pull request overview
Adds “Phase 8” polish to the AI Advisor by introducing a stateless feedback signal path, tightening the procurement-oriented system prompt, and improving assistant-message UX with an action bar for copy + thumbs feedback.
Changes:
- Backend: new auth-guarded
POST /api/v1/chat/feedbackendpoint that validates input and logs structured feedback viaslog. - Backend: expanded procurement framing in the system prompt (urgency tiers + budget justification language) with new prompt-content tests.
- Frontend: assistant message action bar (copy-to-clipboard using Markdown→text stripping + thumbs up/down toggle) and
sendFeedbackclient helper, with new unit tests.
Reviewed changes
Copilot reviewed 16 out of 16 changed files in this pull request and generated 6 comments.
Show a summary per file
| File | Description |
|---|---|
| frontend/src/utils/stripMarkdown.js | Adds Markdown-to-plain-text utility used for clipboard copy. |
| frontend/src/utils/stripMarkdown.test.js | Adds unit tests covering many Markdown stripping cases. |
| frontend/src/services/chatApi.js | Adds fire-and-forget sendFeedback() POST to feedback endpoint. |
| frontend/src/components/chat/ChatMessage.jsx | Adds assistant action bar UI (copy + feedback buttons). |
| frontend/src/components/chat/ChatMessages.jsx | Tracks per-message feedback state and triggers sendFeedback() on toggles. |
| frontend/src/components/chat/ChatPanel.test.jsx | Extends component tests for action bar visibility, copy behavior, and feedback interactions. |
| backend/handlers/feedback.go | Implements ChatFeedback handler with validation + structured logging. |
| backend/handlers/feedback_test.go | Adds handler tests for auth/validation/logging/truncation behaviors. |
| backend/handlers/routes.go | Registers the new /api/v1/chat/feedback route with rate limit tier write. |
| backend/services/ai/prompt.go | Extends procurement framing with urgency tiers and budget justification guidance. |
| backend/services/ai/prompt_test.go | Adds tests to enforce new procurement prompt requirements and avoid calendar references. |
| .planning/phases/08-polish/08-01-SUMMARY.md | Documents plan 08-01 completion (feedback endpoint + prompt tuning). |
| .planning/phases/08-polish/08-02-SUMMARY.md | Documents plan 08-02 completion (action bar + stripMarkdown + sendFeedback). |
| .planning/STATE.md | Records project milestone completion state. |
| .planning/ROADMAP.md | Adds roadmap/phase documentation snapshot. |
| .planning/REQUIREMENTS.md | Adds requirements documentation snapshot. |
Comments suppressed due to low confidence (2)
backend/services/ai/prompt_test.go:137
- The comment says “At least one of these business-impact terms must be present”, but the assertion requires all 3 terms (
found < 3fails). Update the comment to match the test’s actual requirement (or relax the assertion if “at least one” is intended).
lower := strings.ToLower(systemPrompt)
// At least one of these business-impact terms must be present
businessTerms := []string{
"deployment failure",
"sla",
"developer velocity",
}
found := 0
for _, term := range businessTerms {
if strings.Contains(lower, term) {
found++
}
}
if found < 3 {
t.Errorf("system prompt should contain all budget justification terms (deployment failure, SLA, developer velocity), found %d of 3", found)
.planning/phases/08-polish/08-02-SUMMARY.md:10
- This summary claims the feedback endpoint provides “rating persistence”, but the implementation is explicitly stateless (logs via slog without storing). Consider rewording to avoid implying persistence/storage.
requires:
- phase: 08-polish-01
provides: POST /api/v1/chat/feedback endpoint for thumbs up/down rating persistence
- phase: 05-frontend-chat
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
- Fix setTimeout leak on unmount in CopyButton (useRef + cleanup) - Remove unused `within` import from ChatPanel.test.jsx - Use rune-based truncation for UTF-8 safety in feedback handler - Remove hallucination-inducing price estimate language from prompt - Fix misleading test comment (says "at least one" but requires all 3) - Log non-2xx responses in sendFeedback for debuggability
- Remove unused withFeedbackTestAuth (staticcheck U1000), alias feedbackTestClaims to shared testClaims - Replace useEffect setState with render-time state adjustment (react-hooks/set-state-in-effect) - Add clipboard failure feedback with red icon and "Copy failed" label - Fix clearTimeout on rapid copy clicks to prevent premature reset - Move handleFeedback toggle logic inside functional updater to remove feedbackState dependency and reduce React.memo re-renders - Capture parse error message in parseSSEEvent bare catch - Add sendFeedback unit tests for CSRF, credentials, and fire-and-forget
|
/claude-review |
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 17 out of 17 changed files in this pull request and generated 5 comments.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| {showActionBar && ( | ||
| <div | ||
| data-testid="action-bar" | ||
| className="absolute top-2 right-2 md:opacity-0 md:group-hover:opacity-100 transition-opacity flex items-center gap-0.5 bg-slate-800 border border-slate-600 rounded-md px-1 py-0.5" | ||
| > | ||
| <CopyButton content={message.content} /> | ||
| <FeedbackButtons | ||
| rating={feedbackRating} | ||
| onRate={(rating) => onFeedback?.(rating)} | ||
| /> | ||
| </div> |
There was a problem hiding this comment.
The action bar is hidden on desktop using md:opacity-0, but it remains in the tab order and accessible tree. Keyboard users can tab to invisible buttons, and screen readers may announce controls that aren't visibly available. Consider also toggling interactivity/visibility (e.g., md:pointer-events-none when hidden + md:group-hover:pointer-events-auto, and/or md:group-focus-within:opacity-100) or conditionally rendering the action bar only when visible/focused.
There was a problem hiding this comment.
Fixed. Added md:pointer-events-none when hidden, md:group-hover:pointer-events-auto and md:group-focus-within:opacity-100 + pointer-events-auto so keyboard users can tab into the action bar when it becomes visible via focus.
frontend/src/utils/stripMarkdown.js
Outdated
| // Blockquotes | ||
| .replace(/^>\s?/gm, "") | ||
| // Unordered list markers | ||
| .replace(/^\s*[-*+]\s+/gm, "- ") |
There was a problem hiding this comment.
stripMarkdown claims to “strip unordered list markers”, but the regex replaces them with a literal "- " (so list markers still appear in the copied plain text). Either remove the marker entirely (replace with "") or update the doc/tests to reflect that bullets are intentionally preserved/normalized.
| .replace(/^\s*[-*+]\s+/gm, "- ") | |
| .replace(/^\s*[-*+]\s+/gm, "") |
There was a problem hiding this comment.
Fixed. Unordered list markers now stripped entirely for consistency with ordered list handling.
Code Review: Phase 8 Polish — Feedback Endpoint, Prompt Tuning, Action BarOverall this is a well-structured PR with solid test coverage. The design is appropriate for a fire-and-forget analytics signal: no state persistence, auth-guarded, rate-limited, and input-validated. A few issues worth addressing below. Bug: Side effect inside React state updater (
|
| Severity | Issue |
|---|---|
| Bug | sendFeedback called inside React state updater — can double-fire in Strict Mode |
| Minor | _italic_ regex mangles snake_case identifiers in stripMarkdown |
| Minor | Ordered list numbers stripped rather than preserved |
| Minor | Calendar-reference test coverage is incomplete |
| Nit | "write" rate limit may be too tight for feedback; consider a dedicated tier |
The bug is worth fixing before merge; the rest can be addressed as follow-up issues or accepted as-is with a comment.
- Move sendFeedback out of React state updater to prevent duplicate POSTs in StrictMode (updaters must be pure) - Preserve code block content in stripMarkdown by extracting to placeholders before running transformation pipeline - Restrict italic underscore regex to word boundaries to preserve snake_case identifiers like diego_cell_count - Strip unordered list markers entirely for consistency with ordered - Fix action bar a11y: hidden buttons no longer keyboard-focusable (pointer-events-none when hidden, restored on hover/focus-within)
The react-hooks/refs lint rule disallows setting ref.current during render. Move the ref sync into a useEffect keyed on feedbackState.
The ref-based approach introduced a stale-value race condition when synced via useEffect (ref wouldn't update until after commit). Instead, read feedbackState directly in handleFeedback and add it to the useCallback dependency array. Feedback toggles are infrequent so the re-memoization cost is negligible.
Documentation guidelines prohibit em dashes in comments.
Planning artifacts are development aids, not deployable code. Already in .gitignore; this removes them from the git index.
Summary
POST /api/v1/chat/feedback): Auth-guarded endpoint that logs thumbs up/down ratings via structured slog for analytics without persisting state. Includes rate limiting and input validation.Test plan