Skip to content

feat(ai-chat): Codiff-style AI walkthroughs for generated SQL#1948

Merged
datlechin merged 11 commits into
mainfrom
feat/ai-sql-walkthroughs
Jul 26, 2026
Merged

feat(ai-chat): Codiff-style AI walkthroughs for generated SQL#1948
datlechin merged 11 commits into
mainfrom
feat/ai-sql-walkthroughs

Conversation

@datlechin

Copy link
Copy Markdown
Member

Closes #1945.

AI Explain, Optimize, and Fix Error now answer with a reviewable walkthrough in the chat panel instead of plain markdown, so a user can inspect an AI SQL change before trusting it.

What you get

  • Before/after SQL diff for Optimize and Fix, with a Unified / Split toggle (persisted per block). Explain shows the query with the steps anchored to it.
  • Numbered steps with a one-line reason and an importance tag (Critical / Change / Context). Expand a step to see the exact lines it refers to; Jump to lines scrolls to and highlights them.
  • Ask about a step sends a follow-up question that stays anchored to that step.
  • Apply to Editor replaces the editor content with the suggested SQL after a confirmation. Nothing is applied automatically.

Design

The AI writes only the narrative (the rewritten afterSQL plus ordered steps). The app computes the diff itself from the original query, so the diff always reflects the real change and a model cannot show a diff that does not exist.

Structured output travels as a fenced JSON envelope parsed out of the streamed text, not tool calling. This works on every provider including the local ones (Ollama, llama.cpp, MLX), and degrades to plain markdown when a model ignores the format: the fence is stripped so no raw JSON shows, and the response renders as normal text.

Native and HIG details: semantic red/green with +/- gutter markers (never color alone), DisclosureGroup steps, reduce-motion gated highlight, VoiceOver status on diff rows, confirm-before-apply per Apple's Generative AI guidance. Anchors that fall out of range render no highlight rather than pointing at the wrong line.

Structure

Three commits:

  1. refactor(datagrid) extracts the line-diff engine (DiffComputer/DiffPair) out of FileConflictDiffSheet into Core/Diff/SqlDiff.swift, adding unified-diff output and SQL normalization. The conflict sheet consumes it unchanged.
  2. feat(ai-chat) adds the .sqlWalkthrough chat block (additive wire Codable, old conversations still decode) and AIChatWalkthroughBlockView, and encodes the walkthrough as proposed-SQL text in every provider's history so follow-ups keep context.
  3. feat(ai-chat) adds the envelope parser, the three walkthrough prompt templates, and the view-model wiring, plus CHANGELOG and docs.

Tests

New unit tests: envelope parsing (well-formed, code-fenced, no-close-fence, malformed, no-fence, null afterSQL, unique step ids, stripFence), diff extraction parity and unified diff, SQL normalization, anchor validity (in-range / out-of-range / nil), and .sqlWalkthrough wire round-trip plus legacy decode. UI automation is deferred: the flow depends on non-deterministic AI responses and needs a mock provider injection point.

Verified locally: build succeeds, and all four new suites pass.

Scope note

In-editor cross-highlighting (highlighting the anchored lines in the main editor, not just the in-chat diff) is intentionally left out of this first version to keep the panel decoupled from editor state. Worth a follow-up.

@mintlify

mintlify Bot commented Jul 22, 2026

Copy link
Copy Markdown

Preview deployment for your docs. Learn more about Mintlify Previews.

Project Status Preview Updated (UTC)
TablePro 🟢 Ready View Preview Jul 22, 2026, 5:07 PM

💡 Tip: Enable Workflows to automatically generate PRs for you.

@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: 72988d7bb1

ℹ️ 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".

: String(localized: "Replace the editor content with the suggested SQL"))
}
.alert(String(localized: "Apply suggested SQL?"), isPresented: $showApplyConfirmation) {
Button(String(localized: "Apply")) { actions?.insertQueryFromAI(afterSQL) }

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Replace the active query when applying a walkthrough

For the normal Optimize/Fix flow, the selected editor tab already contains the query being reviewed. insertQueryFromAI only mutates an empty selected query tab and otherwise opens a new query tab (MainContentCoordinator.swift:1017-1036), so confirming this action does not perform the replacement promised by the button and confirmation text; it leaves the original editor content unchanged.

Useful? React with 👍 / 👎.

Comment on lines +229 to +231
func resolveWalkthroughIfNeeded(id: UUID) {
guard let beforeSQL = pendingWalkthroughBeforeSQL else { return }
pendingWalkthroughBeforeSQL = nil

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 Retain walkthrough context for regeneration and retry

A successful walkthrough clears the only stored beforeSQL here, but regenerate() deletes that assistant turn and restarts streaming without restoring it; likewise the error path clears it before retry(). The hidden prompt still instructs the model to emit the envelope, but resolveWalkthroughIfNeeded then returns immediately, so regenerated/retried Explain, Optimize, and Fix responses display their raw JSON instead of the walkthrough UI.

Useful? React with 👍 / 👎.

Comment on lines +448 to +450
private func activateAnchor(_ anchor: SqlWalkthroughAnchor) {
activeAnchor = anchor
scrollTarget = unifiedScrollTarget(for: anchor)

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 Use split-row targets when the split diff is selected

When users select Split and press “Jump to lines,” this always calculates an ID from the unified diff. Split rows are identified by their DiffPair index, while unified IDs gain an extra entry for each changed pair, so after any prior replacement the target either scrolls to a different row or does not exist in the split view. The highlighted lines are correct, but the advertised jump action is not.

Useful? React with 👍 / 👎.

@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: 22d7563d4e

ℹ️ 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 +155 to +157
func sendWithWalkthroughContext(prompt: String, beforeSQL: String) {
pendingWalkthroughBeforeSQL = beforeSQL
sendWithContext(prompt: prompt)

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 Clear walkthrough state when the request does not start

When a walkthrough command cannot start—for example, no provider is configured, the connection policy is never, or preflight rejects an oversized prompt—this state remains set because none of those early exits clears it. The next ordinary chat message then receives the walkthrough system directive and is parsed against the stale SQL, so a normal response can be rendered as an unrelated SQL walkthrough.

Useful? React with 👍 / 👎.

Comment on lines +124 to +125
private func unifiedDiff(before: [String], after: [String]) -> some View {
let lines = Array(DiffComputer.computeUnified(before: before, after: after).prefix(Self.maxDiffLines))

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 Count rendered unified diff rows when truncating

For a query with 500 changed lines, computeUnified emits 1,000 display rows (a removal and addition per change), but this prefixes the result at 500 while the footer compares against max(before.count, after.count), also 500. Consequently the entire added half of the diff is silently hidden with no truncation notice, preventing users from reviewing the proposed SQL before applying it.

Useful? React with 👍 / 👎.

@mintlify

mintlify Bot commented Jul 22, 2026

Copy link
Copy Markdown

Preview deployment for your docs. Learn more about Mintlify Previews.

Project Status Preview Updated (UTC)
TablePro 🟡 Building Jul 22, 2026, 5:05 PM

💡 Tip: Enable Workflows to automatically generate PRs for you.

@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: fdb88ae868

ℹ️ 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 +259 to +262
guard case .text(let openText) = tail[0].kind else { return }
let prose = WalkthroughEnvelopeParser.stripFence(from: openText)
let consumedIDs = Set(tail.dropFirst().map(\.id))
messages[idx].blocks.removeAll { consumedIDs.contains($0.id) }

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 Preserve malformed walkthrough responses

When a model emits the opening marker but returns invalid or truncated JSON (a realistic streaming failure), this removes the opening block and every later text block before WalkthroughEnvelopeParser.parse is checked. The subsequent parse failure then leaves users with only the prose before the marker and discards the proposed SQL/steps entirely; parse first and mutate the blocks only on success so malformed model output remains visible as normal text.

Useful? React with 👍 / 👎.

Signed-off-by: Ngô Quốc Đạt <datlechin@gmail.com>
@datlechin
datlechin merged commit c027283 into main Jul 26, 2026
2 of 3 checks passed
@datlechin
datlechin deleted the feat/ai-sql-walkthroughs branch July 26, 2026 08:31

@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: b725cf34ca

ℹ️ 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".

assistantID: assistantID,
settings: settings
settings: settings,
includeWalkthroughDirective: self.pendingWalkthroughBeforeSQL != nil

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Recreate Copilot conversations when the directive changes

With GitHub Copilot, this walkthrough flag is dynamic per request, but CopilotChatProvider.streamChat only applies options.systemPrompt when conversationId == nil (CopilotChatProvider.swift:73-80). Consequently, a slash walkthrough issued in an existing Copilot conversation never receives the envelope directive; conversely, after a walkthrough creates a new conversation, later ordinary follow-ups remain governed by that directive and can expose raw walkthrough JSON because no walkthrough is pending for parsing. Reset or recreate the Copilot conversation when this flag changes.

Useful? React with 👍 / 👎.

Comment on lines +161 to +163
var transcriptText: String {
guard let afterSQL = envelope.afterSQL, !afterSQL.isEmpty else { return "" }
return "Proposed SQL:\n\(afterSQL)"

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 Preserve walkthrough steps in provider history

When a user types a normal follow-up such as “explain step 2,” this transcript retains only the proposed SQL and discards every step title, reason, importance, and anchor; Explain walkthroughs are omitted entirely because afterSQL is nil. Stateless providers therefore receive no assistant history describing the step being referenced, so follow-ups cannot reliably remain anchored to the walkthrough. Serialize the step narrative as well as the proposed SQL.

Useful? React with 👍 / 👎.

sql
.replacingOccurrences(of: "\r\n", with: "\n")
.replacingOccurrences(of: "\r", with: "\n")
.trimmingCharacters(in: .whitespacesAndNewlines)

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 Keep source line numbering stable for anchors

When the selected query begins with one or more blank lines, the model sees and numbers those lines in the prompt, but this normalization removes them before anchor validation, snippets, and highlighting. An otherwise valid anchor can therefore highlight a later, unrelated SQL line—for example, source line 2 becomes normalized line 1. Preserve boundary lines for anchor geometry, or normalize the query before sending it to the model.

Useful? React with 👍 / 👎.

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.

Consider Codiff-style AI walkthroughs for generated SQL

1 participant