feat: dashboard chat reply is the agent's own words, not a canned string - #102
Merged
Merged
Conversation
The dashboard build contract forbade prose ("no prose"), and both the
backend and the frontend hardcoded the same reply on every completed
build: "Done — built and verified against your data." Every turn showed
the identical line regardless of what the user asked — including turns
that were corrections ("you're showing 3k, I expected 6k+"), where a
blanket "built and verified" claimed success over a change that may not
have landed. Worse than templated: a confident false claim.
The contract now ends with a required ```dashboard-note``` fence — 1-3
sentences in plain business language stating what THIS turn changed,
what it could not do or verify, and (on a correction) whether the
disputed value actually moved. It is bound by the existing
"never show internals" security rule, so no table/column/SQL/UUID leaks
into the reply.
- DashboardAgentService: NOTE_FENCE + extractSummary/stripNotes; the
note is stripped BEFORE HTML extraction so prose can't be mistaken for
the artifact; summary rides in config.summary.
- SavedDashboardService.buildReplyText and the frontend store's
buildReplyText both render the note, falling back to the (now shared)
DEFAULT_BUILD_REPLY constant only when a turn produced no note — so an
agent still on the old contract keeps working.
Verified end-to-end against the live stack (real build + a correction
turn + chat-only path), DB read-back, and browser: the reply now differs
per turn, addresses corrections directly, leaks no internals, and
survives reload.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Collaborator
Author
Screenshots — the reply is now the agent's own wordsThe chat thread (one build turn, then a correction turn) — the two cases that show the fix:
Full workspace — the reply and the dashboard it produced side by side (note the KPI actually changed from 20 → 0 to match the words): Captured live through the browser (chrome-devtools MCP) against the local stack; no internals (table/column/SQL/UUID) appear in any reply. Regression suite statusRan the
All green. Backend recompiled cleanly ( |
venkateshsakamuri-lab
approved these changes
Sep 8, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.



Problem
The dashboard build chat replied with the same hardcoded string on every completed build — "Done — built and verified against your data. Saved as a draft — tell me what to change." — regardless of what the user said. It showed up identically against unrelated messages and against corrections. In the reported case a user said the dashboard showed ~3k active hotels when they expected 6k+, and the reply was still a blanket "built and verified": a confident claim of success over a change that may never have landed. That's worse than templated.
Root cause: the agent's task contract literally said "no prose", and both the backend (
SavedDashboardService.java) and the frontend store (useDashboardChatStore.js) hardcoded the constant. There was no agent reply to show — the model was forbidden from speaking.Change
DashboardAgentService): the final message now ends with a required```dashboard-notefence — 1–3 sentences in plain business language: what this turn changed, anything it could not do/verify or deliberately skipped, and on a correction whether the disputed value actually moved. Bound by the existing "never show internals" security rule (no table/column/SQL/UUID in the reply).extractSummaryreads it;stripNotesremoves it before HTML extraction so prose can't be mistaken for the artifact. The note rides inconfig.summary.SavedDashboardService.buildReplyText(persistence) and the frontend store'sbuildReplyText(live tab) both render the note, falling back to a now-sharedDEFAULT_BUILD_REPLYconstant only when a turn produced no note — so an agent still on the old contract keeps working. The two helpers are kept textually in sync (documented in CLAUDE.md).Verification (live stack, not code-reading)
Real build + a correction turn + chat-only path, driven through the browser (chrome-devtools MCP), with DB read-back and network confirmation that calls hit the local backend:
deepsql.query); chat-only path still returns the agent's words and never appends the canned line.Full hands-on QA: 11/11 scenarios PASS, zero blocking issues.
Notes
BUILD SUCCESS); frontend lint clean.AGENT_WEBUI_URL=http://deepsql-agent:8787(the compose default). A local.envwithhttp://localhost:8787makes every build fail as "agent unavailable" — worth aligning.env.example/docs, but outside this PR's committed files.🤖 Generated with Claude Code