Skip to content

fix(cli): navigate artifact on fork source links - #786

Merged
ilyabo merged 1 commit into
mainfrom
codex/fix-artifact-chat-handoff
Jul 3, 2026
Merged

ilyabo merged 1 commit into
mainfrom
codex/fix-artifact-chat-handoff

Conversation

@ilyabo

@ilyabo ilyabo commented Jul 2, 2026 •

Copy link
Copy Markdown
Collaborator

Summary

Fixes CLI artifact chat handoff navigation after an AI-created worksheet/block document fork.

  • Adds an optional fork-source switch handler to shared chat messages so host apps can coordinate non-chat state when following fork provenance.
  • Wires the CLI app to select the source session's owning artifact before switching to that source chat, preventing artifact/chat auto-sync from immediately switching back to the forked target.
  • Adds focused coverage for the artifact-first session switch behavior.

Root Cause

The fork provenance link only switched AI sessions. In the CLI app, artifact AI sync is driven by the current artifact. When the current artifact was still the fork target, switching to the source chat alone was immediately overwritten by sync, so the link appeared to do nothing and subsequent prompts could continue targeting the forked worksheet.

Validation

  • pnpm --filter sqlrooms-cli-app test -- --runTestsByPath src/__tests__/artifactAiNavigation.test.ts
  • pnpm --filter @sqlrooms/ai-core typecheck
  • pnpm --filter sqlrooms-cli-app typecheck
  • pnpm exec prettier --check packages/ai-core/src/components/ChatMessagesContainer.tsx apps/sqlrooms-cli-ui/src/components/AssistantChatContainer.tsx apps/sqlrooms-cli-ui/src/artifactAiNavigation.ts apps/sqlrooms-cli-ui/src/__tests__/artifactAiNavigation.test.ts
  • pnpm build
  • Pre-push hook: knip, turbo typecheck, circular dependency check, turbo test

Summary by CodeRabbit

  • New Features
    • Added artifact-aware AI session switching so chat history can return to the correct artifact/worksheet context.
    • Enhanced artifact-scoped AI chat handoff to optionally copy source chart blocks into an empty target worksheet.
    • Added UI support for switching back to the fork source from chat provenance.
  • Bug Fixes
    • Prevented worksheet AI runs from reporting success when no worksheet mutation occurred (with automatic retry prompting).
    • Improved fallback behavior when sessions/artifacts are missing to avoid incorrect navigation.

@coderabbitai

coderabbitai Bot commented Jul 2, 2026 •

Copy link
Copy Markdown
Contributor

Review Change Stack

Warning

Review limit reached

@ilyabo, you've reached your PR review limit, so we couldn't start this review.

Next review available in: 5 minutes

Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available.
You're only billed for reviews past your plan's rate limits ($0.25/file).

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 179c0381-313b-487b-a866-2cbddf26e07f

📥 Commits

Reviewing files that changed from the base of the PR and between cca4566 and f7051fe.

📒 Files selected for processing (11)
  • apps/sqlrooms-cli-ui/src/__tests__/artifactChatHandoff.test.ts
  • apps/sqlrooms-cli-ui/src/ai/__tests__/createWorksheetAgentTool.test.ts
  • apps/sqlrooms-cli-ui/src/ai/createWorksheetAgentTool.ts
  • apps/sqlrooms-cli-ui/src/artifactChatHandoff.ts
  • apps/sqlrooms-cli-ui/src/components/AssistantChatContainer.tsx
  • packages/ai-core/src/components/ChatMessagesContainer.tsx
  • packages/artifacts/README.md
  • packages/artifacts/__tests__/artifactAiChatHandoff.test.ts
  • packages/artifacts/src/ai/artifactAiChatHandoff.ts
  • packages/artifacts/src/ai/artifactAiNavigation.ts
  • packages/artifacts/src/ai/index.ts
📝 Walkthrough

Walkthrough

Adds artifact-scoped AI session switching, chart-block copying during artifact chat handoff, worksheet mutation retry logic in the worksheet agent tool, and fork-source chat UI wiring, with tests covering the new behaviors.

Changes

Artifact navigation, handoff, worksheet agent, and chat UI

Layer / File(s) Summary
Artifact-scoped session switching
apps/sqlrooms-cli-ui/src/artifactAiNavigation.ts, apps/sqlrooms-cli-ui/src/__tests__/artifactAiNavigation.test.ts
Adds switchToArtifactAiSession, which checks session ownership, updates the current artifact when available, then switches the AI session; tests cover success, stale ownership, and unknown sessions.
Chart-block handoff copying
apps/sqlrooms-cli-ui/src/artifactChatHandoff.ts, apps/sqlrooms-cli-ui/src/__tests__/artifactChatHandoff.test.ts
Detects chart-copy requests from the latest user message, carries a copy flag through handoff state, clones source chart blocks into an empty target, and updates session, draft-context, and current-artifact state.
Worksheet mutation retry
apps/sqlrooms-cli-ui/src/ai/createWorksheetAgentTool.ts, apps/sqlrooms-cli-ui/src/ai/__tests__/createWorksheetAgentTool.test.ts
Tracks initial worksheet block count and mutation-tool calls, retries the sub-agent with a stricter prompt when no worksheet mutation occurs, and fails if the second attempt still does not mutate the worksheet.
Fork-source chat UI wiring
packages/ai-core/src/components/ChatMessagesContainer.tsx, apps/sqlrooms-cli-ui/src/components/AssistantChatContainer.tsx
Adds an onSwitchToForkSource callback type/prop, updates fork provenance interaction and layout, and routes chat history and fork-source clicks through artifact-aware session switching.

Estimated code review effort: 3 (Moderate) | ~30 minutes

Possibly related PRs

  • sqlrooms/sqlrooms#707: Adds the per-session draft-context state APIs used when the handoff flow clears and resets draft context.
  • sqlrooms/sqlrooms#716: Covers chat fork provenance and source-session switching behavior in the same chat messages component.
  • sqlrooms/sqlrooms#747: Implements the artifact chat handoff flow that this PR extends with chart-block copying.

Suggested reviewers: dmitriy-kostianetskiy, bdjulbic

Sequence Diagram(s)

sequenceDiagram
  participant Caller
  participant switchToArtifactAiSession
  participant RoomState

  Caller->>switchToArtifactAiSession: sessionId
  switchToArtifactAiSession->>RoomState: check ai.config.sessions[sessionId]
  alt session exists
    switchToArtifactAiSession->>RoomState: setCurrentArtifact(ownerArtifactId)
    switchToArtifactAiSession->>RoomState: ai.switchSession(sessionId)
    switchToArtifactAiSession-->>Caller: true
  else session missing
    switchToArtifactAiSession-->>Caller: false
  end
Loading
sequenceDiagram
  participant commandMiddleware
  participant SourceArtifact
  participant onChatFinish
  participant TargetArtifact

  commandMiddleware->>SourceArtifact: load source session and last user message
  commandMiddleware->>commandMiddleware: compute shouldCopySourceChartBlocks
  commandMiddleware->>SourceArtifact: setSessionArtifact, clear draft context, set current artifact
  onChatFinish->>onChatFinish: allow source or target current artifact
  alt shouldCopySourceChartBlocks
    onChatFinish->>SourceArtifact: getBlocks(chart)
    onChatFinish->>TargetArtifact: append cloned chart blocks
  end
  onChatFinish->>TargetArtifact: reset draft context, set current artifact, select latest session
Loading
sequenceDiagram
  participant execute
  participant BlockDocument
  participant runWorksheetSubAgent

  execute->>BlockDocument: ensureBlockDocument, getBlocks(initial count)
  execute->>runWorksheetSubAgent: attempt 1
  runWorksheetSubAgent-->>execute: result + tool calls
  execute->>BlockDocument: getBlocks(recheck count)
  execute->>execute: hasWorksheetMutationToolCall?
  alt no mutation detected
    execute->>runWorksheetSubAgent: attempt 2 with augmented prompt
    runWorksheetSubAgent-->>execute: result + tool calls
    execute->>execute: recheck mutation
  end
Loading
sequenceDiagram
  participant User
  participant ChatMessagesContainer
  participant AssistantChatContainer
  participant switchToArtifactAiSession

  User->>ChatMessagesContainer: click fork provenance
  ChatMessagesContainer->>AssistantChatContainer: onSwitchToForkSource(sourceSession, forkOrigin)
  AssistantChatContainer->>switchToArtifactAiSession: switch to source session
  switchToArtifactAiSession-->>AssistantChatContainer: current artifact and session updated
Loading
🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately summarizes the CLI fix for navigating to the artifact when following fork source links.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch codex/fix-artifact-chat-handoff

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@ilyabo
ilyabo force-pushed the codex/fix-artifact-chat-handoff branch 2 times, most recently from 2b92470 to b58b8fc Compare July 2, 2026 10:52
@ilyabo
ilyabo marked this pull request as ready for review July 3, 2026 15:53
@ilyabo
ilyabo force-pushed the codex/fix-artifact-chat-handoff branch from b58b8fc to cca4566 Compare July 3, 2026 15:57

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

ℹ️ 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 +451 to +453
if (
currentBlockCount === initialBlockCount &&
!hasWorksheetMutationToolCall(result.agentToolCalls)

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 read-only worksheet requests successful

This new no-mutation check also applies to read-only worksheet requests. The CLI system prompt tells the model to use block_document_agent when the primary artifact is a worksheet, including when the user asks to inspect it, so a valid request like “what columns are in this worksheet?” can legitimately only call query/list/schema tools and return text. With this condition, that successful answer is retried and then converted into an error unless a block is mutated, which breaks read-only worksheet Q&A and can pressure the agent to add unwanted content.

Useful? React with 👍 / 👎.

Comment on lines +276 to +277
if (handoff.shouldCopySourceChartBlocks) {
copySourceChartBlocksToEmptyTarget(

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 Restrict chart copying to block-document targets

This branch can run for any handoff target in CLI_ARTIFACT_TYPES, which includes non-block-document artifacts such as dashboards, SQL queries, HTML apps, and Python artifacts. For a prompt like “copy the chart to a new dashboard”, blockDocuments.getBlocks(targetArtifact.id) returns empty and appendBlocks creates orphan block-document content under the dashboard id instead of updating the dashboard, polluting workspace state with invisible content. Please guard this copy path to worksheet/block-document targets before appending blocks.

Useful? React with 👍 / 👎.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 2

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
apps/sqlrooms-cli-ui/src/ai/createWorksheetAgentTool.ts (1)

427-467: 🗄️ Data Integrity & Integration | 🟡 Minor | ⚡ Quick win

Metadata only reflects the final attempt's tool calls.

When a retry occurs, result is overwritten by the second runWorksheetSubAgent call, so calculateAgentResultMetadata (line 464-467) only sees the second run's agentToolCalls. Any tool calls (e.g. exploratory queries) from the discarded first attempt are silently dropped from usage/cost metadata, undercounting actual work done for that request.

💡 Proposed fix to accumulate tool calls across retries
-        let result: AgentRunResult = await runWorksheetSubAgent(intent);
+        let result: AgentRunResult = await runWorksheetSubAgent(intent);
+        let allToolCalls = result.agentToolCalls ?? [];
         let currentBlockCount =
           blockDocumentAdapter.getBlocks(worksheetId)?.length ?? 0;

         if (
           currentBlockCount === initialBlockCount &&
           !hasWorksheetMutationToolCall(result.agentToolCalls)
         ) {
           result = await runWorksheetSubAgent(`${intent}
 ...`);
+          allToolCalls = allToolCalls.concat(result.agentToolCalls ?? []);
           currentBlockCount =
             blockDocumentAdapter.getBlocks(worksheetId)?.length ?? 0;
         }
...
-        const metadata = calculateAgentResultMetadata(
-          undefined,
-          result.agentToolCalls,
-        );
+        const metadata = calculateAgentResultMetadata(undefined, allToolCalls);
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@apps/sqlrooms-cli-ui/src/ai/createWorksheetAgentTool.ts` around lines 427 -
467, The metadata calculation in createWorksheetAgentTool is only using the
final retry’s tool calls because runWorksheetSubAgent overwrites result on the
second attempt. Preserve and merge agentToolCalls from both attempts before
calling calculateAgentResultMetadata, so exploratory calls from the first run
are included in the final usage/cost metadata. Keep the retry logic intact in
runWorksheetSubAgent, but aggregate the first and second AgentRunResult.tool
calls instead of discarding the initial result.
🧹 Nitpick comments (2)
apps/sqlrooms-cli-ui/src/__tests__/artifactAiNavigation.test.ts (1)

6-47: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Add a test for sessions without an owning artifact.

All three tests set sessionArtifactId to a truthy value; none cover the case where getSessionArtifactId returns undefined (e.g., a legacy/global session), which skips setCurrentArtifact entirely. Consider adding a case for that branch.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@apps/sqlrooms-cli-ui/src/__tests__/artifactAiNavigation.test.ts` around lines
6 - 47, Add a test case in artifactAiNavigation.test.ts that covers the branch
where artifactAi.getSessionArtifactId returns undefined, since createMockStore
currently only exercises truthy sessionArtifactId values. Update or extend the
existing createMockStore helper and related artifactAi navigation test(s) so the
session-selection flow is verified for a legacy/global session and confirms
setCurrentArtifact is skipped while switchSession still behaves as expected.
apps/sqlrooms-cli-ui/src/__tests__/artifactChatHandoff.test.ts (1)

171-229: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Add a test for a non-chart-copy prompt with an empty target.

Both tests use a "same chart" prompt. Add a case with a prompt that doesn't match isSameChartRequest (empty target) to confirm appendBlocks is skipped due to shouldCopySourceChartBlocks being false, not just because the target already has blocks.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@apps/sqlrooms-cli-ui/src/__tests__/artifactChatHandoff.test.ts` around lines
171 - 229, The current tests only cover same-chart handoff behavior, so add a
case in createArtifactChatHandoffController that uses a prompt not matched by
isSameChartRequest while the target artifact is still empty. Reuse
triggerHandoff and createState, then assert appendBlocks is not called because
shouldCopySourceChartBlocks is false, confirming the skip happens for a
non-chart-copy prompt rather than only when targetBlocks already exist.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@apps/sqlrooms-cli-ui/src/ai/__tests__/createWorksheetAgentTool.test.ts`:
- Around line 10-18: The test mock for BlockDocumentAiAdapter.addBlock is
letting the block parameter default to unknown, which breaks type-checking when
accessing block.id. Update the addBlock jest.fn in
createWorksheetAgentTool.test.ts to explicitly type its parameters using the
addBlock signature from BlockDocumentAiAdapter so block is no longer unknown,
and keep the mock behavior of pushing the block and returning block.id.

In `@packages/ai-core/src/components/ChatMessagesContainer.tsx`:
- Around line 14-18: Add TSDoc to the exported ChatForkSourceSwitchHandler type
in ChatMessagesContainer.tsx so this new public API in `@sqlrooms/ai-core` is
documented per the package guidelines. Place a concise description above the
type alias explaining what the handler is used for, and mention the parameters
sourceSession and forkOrigin so the exported surface is properly documented
alongside the other public types.

---

Outside diff comments:
In `@apps/sqlrooms-cli-ui/src/ai/createWorksheetAgentTool.ts`:
- Around line 427-467: The metadata calculation in createWorksheetAgentTool is
only using the final retry’s tool calls because runWorksheetSubAgent overwrites
result on the second attempt. Preserve and merge agentToolCalls from both
attempts before calling calculateAgentResultMetadata, so exploratory calls from
the first run are included in the final usage/cost metadata. Keep the retry
logic intact in runWorksheetSubAgent, but aggregate the first and second
AgentRunResult.tool calls instead of discarding the initial result.

---

Nitpick comments:
In `@apps/sqlrooms-cli-ui/src/__tests__/artifactAiNavigation.test.ts`:
- Around line 6-47: Add a test case in artifactAiNavigation.test.ts that covers
the branch where artifactAi.getSessionArtifactId returns undefined, since
createMockStore currently only exercises truthy sessionArtifactId values. Update
or extend the existing createMockStore helper and related artifactAi navigation
test(s) so the session-selection flow is verified for a legacy/global session
and confirms setCurrentArtifact is skipped while switchSession still behaves as
expected.

In `@apps/sqlrooms-cli-ui/src/__tests__/artifactChatHandoff.test.ts`:
- Around line 171-229: The current tests only cover same-chart handoff behavior,
so add a case in createArtifactChatHandoffController that uses a prompt not
matched by isSameChartRequest while the target artifact is still empty. Reuse
triggerHandoff and createState, then assert appendBlocks is not called because
shouldCopySourceChartBlocks is false, confirming the skip happens for a
non-chart-copy prompt rather than only when targetBlocks already exist.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: a1d0ebbb-5f75-420e-bda7-d8d4b4978617

📥 Commits

Reviewing files that changed from the base of the PR and between 3997caf and b58b8fc.

📒 Files selected for processing (8)
  • apps/sqlrooms-cli-ui/src/__tests__/artifactAiNavigation.test.ts
  • apps/sqlrooms-cli-ui/src/__tests__/artifactChatHandoff.test.ts
  • apps/sqlrooms-cli-ui/src/ai/__tests__/createWorksheetAgentTool.test.ts
  • apps/sqlrooms-cli-ui/src/ai/createWorksheetAgentTool.ts
  • apps/sqlrooms-cli-ui/src/artifactAiNavigation.ts
  • apps/sqlrooms-cli-ui/src/artifactChatHandoff.ts
  • apps/sqlrooms-cli-ui/src/components/AssistantChatContainer.tsx
  • packages/ai-core/src/components/ChatMessagesContainer.tsx

Comment thread apps/sqlrooms-cli-ui/src/ai/__tests__/createWorksheetAgentTool.test.ts Outdated
Comment thread packages/ai-core/src/components/ChatMessagesContainer.tsx
@ilyabo
ilyabo force-pushed the codex/fix-artifact-chat-handoff branch from cca4566 to 25a307a Compare July 3, 2026 16:13
@ilyabo
ilyabo force-pushed the codex/fix-artifact-chat-handoff branch from 25a307a to f7051fe Compare July 3, 2026 16:48
@ilyabo
ilyabo merged commit 0c571ca into main Jul 3, 2026
4 checks passed
@ilyabo
ilyabo deleted the codex/fix-artifact-chat-handoff branch July 3, 2026 16:56
@ilyabo
ilyabo restored the codex/fix-artifact-chat-handoff branch July 3, 2026 16:57

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

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

/\b(add|append|build|copy|create|delete|duplicate|edit|embed|graph|insert|make|modify|move|plot|put|remove|replace|update|visuali[sz]e)\b/.test(
normalized,
) ||
/\b(chart|dashboard|data table explorer|html app|map|text block|worksheet block)\b/.test(

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 Allow read-only chart inspection intents

The new heuristic still treats any intent containing chart/dashboard as requiring a worksheet mutation unless it starts with the small allow-list above. For read-only worksheet prompts such as “show me the chart in this worksheet” or “summarize this dashboard block”, the sub-agent can correctly answer by listing/querying blocks without changing the document, but this check retries it and then returns a no-mutation error. Fresh evidence is this keyword branch, which still catches read-only chart/dashboard inspection phrasing that the added what columns... test does not cover.

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.

1 participant