Skip to content

Implement canvas drift detection in Copilot workflow#2265

Open
anthhub wants to merge 1 commit intomainfrom
fix/cavans-draft
Open

Implement canvas drift detection in Copilot workflow#2265
anthhub wants to merge 1 commit intomainfrom
fix/cavans-draft

Conversation

@anthhub
Copy link
Contributor

@anthhub anthhub commented Mar 3, 2026

  • Added computeCanvasDrift function to analyze discrepancies between workflow plans and canvas nodes, identifying tasks missing from the canvas and nodes added outside the plan.
  • Integrated drift detection into the get_workflow_summary method, returning a summary of drift status and details when discrepancies are found.
  • Updated documentation for get_workflow_summary to include new canvas drift information and handling instructions for users.
  • Enhanced the tool selection guide to clarify when to use get_canvas_snapshot based on drift detection results.

This update improves the accuracy of workflow management by providing users with insights into potential inconsistencies between their plans and the actual canvas state.

Summary by CodeRabbit

  • New Features

    • Introduced canvas drift detection to identify synchronization misalignments between plans and canvas states.
    • Added validation preventing patch application when drift is detected, returning detailed error messages.
    • Workflow responses now include drift status information with severity guidance.
  • Documentation

    • Updated guidance documenting drift handling procedures and decision criteria for remediation actions.

- Added `computeCanvasDrift` function to analyze discrepancies between workflow plans and canvas nodes, identifying tasks missing from the canvas and nodes added outside the plan.
- Integrated drift detection into the `get_workflow_summary` method, returning a summary of drift status and details when discrepancies are found.
- Updated documentation for `get_workflow_summary` to include new canvas drift information and handling instructions for users.
- Enhanced the tool selection guide to clarify when to use `get_canvas_snapshot` based on drift detection results.

This update improves the accuracy of workflow management by providing users with insights into potential inconsistencies between their plans and the actual canvas state.
@coderabbitai
Copy link

coderabbitai bot commented Mar 3, 2026

📝 Walkthrough

Walkthrough

This PR introduces Canvas Drift Detection, a new feature that compares planned workflow tasks against canvas nodes to identify synchronization mismatches. Drift checks are integrated into patch and generate workflows, validating that targeted tasks still exist before applying changes. Drift metadata is surfaced in workflow summary responses, and prompt templates are updated to guide drift-aware handling.

Changes

Cohort / File(s) Summary
Canvas Drift Detection Logic
packages/agent-tools/src/copilot/index.ts
Introduces CanvasDrift type and computeCanvasDrift function to compare plan tasks against canvas nodes. Adds drift pre-checks in PatchWorkflow and GenerateWorkflow flows to validate task existence. Extends GetWorkflowSummary and GetWorkflowSnapshot responses with optional canvasDrift metadata when detected.
Prompt Template Updates
packages/skill-template/src/prompts/templates/copilot-agent-system.md
Documents canvas drift handling in workflow summaries and snapshots. Extends guidance with drift-aware behavior rules: when drift is detected, fetch canvas snapshot and either patch or regenerate based on severity. Exposes canvasDrift fields (hasDrift, summary, missingFromCanvas, addedOnCanvas) in response documentation.

Sequence Diagram

sequenceDiagram
    participant Client
    participant Workflow
    participant Canvas as Canvas Storage
    participant DriftDetector as Drift Detector
    participant Summary as Workflow Summary

    Client->>Workflow: Request patch/generate with canvasId
    Workflow->>Canvas: Fetch canvas data & skillResponse nodes
    Canvas-->>Workflow: Return canvas nodes
    Workflow->>DriftDetector: computeCanvasDrift(plan, canvasNodes)
    DriftDetector->>DriftDetector: Compare tasks vs nodes
    DriftDetector-->>Workflow: Return CanvasDrift{hasDrift, counts, items, summary}
    
    alt Drift Detected
        Workflow->>Workflow: Validate patch targets exist
        alt Targets Missing
            Workflow-->>Client: Error with drift details
        else Targets OK
            Workflow->>Summary: Apply patch/generate with canvasDrift
            Summary-->>Client: Return summary + canvasDrift metadata
        end
    else No Drift
        Workflow->>Summary: Apply patch/generate
        Summary-->>Client: Return summary
    end
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related PRs

Suggested reviewers

  • mrcfps
  • CH1111

Poem

🐰 A drift through the canvas, we now can see,
Tasks checked and balanced in harmony,
When plans diverge from nodes on screen,
We warn before patches intervene,
Hop hop — sync detection, clever and keen! 🌿

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately describes the main change: implementing canvas drift detection in the Copilot workflow system. It is specific, concise, and directly reflects the primary functionality added across both the code and documentation changes.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
  • 📝 Generate docstrings (stacked PR)
  • 📝 Generate docstrings (commit on current branch)
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch fix/cavans-draft

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 and usage tips.

@cloudflare-workers-and-pages
Copy link

Deploying refly-branch-test with  Cloudflare Pages  Cloudflare Pages

Latest commit: 2208691
Status: ✅  Deploy successful!
Preview URL: https://487f4a35.refly-branch-test.pages.dev
Branch Preview URL: https://fix-cavans-draft.refly-branch-test.pages.dev

View logs

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

🧹 Nitpick comments (5)
packages/agent-tools/src/copilot/index.ts (4)

44-52: Consider extracting taskId extraction to a helper.

The pattern for extracting taskId from node metadata is repeated multiple times (lines 47, 60, 66, and 250-252). A small helper would improve readability and reduce duplication.

♻️ Optional helper extraction
function getNodeTaskId(node: CanvasNode): string | undefined {
  return (node.data?.metadata as Record<string, unknown>)?.taskId as string | undefined;
}

Then usage becomes:

-const taskId = (node.data?.metadata as Record<string, unknown>)?.taskId as string | undefined;
+const taskId = getNodeTaskId(node);
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/agent-tools/src/copilot/index.ts` around lines 44 - 52, Extract the
repeated taskId extraction into a small helper (e.g., getNodeTaskId) and replace
the inline casts in places that read taskId from a node (examples: the loop over
canvasNodes that sets canvasTaskIds, other occurrences at the locations
currently using the same pattern) with calls to this helper; ensure the helper
signature returns string | undefined and internally does the safe optional
chaining and cast ((node.data?.metadata as Record<string, unknown>)?.taskId as
string | undefined) so callers like canvasTaskIds.set(taskId, node) and any
conditional checks use the helper instead of duplicating the expression.

38-91: Add JSDoc documentation for the function.

Per coding guidelines, functions should have JSDoc style comments. This is an internal utility but documenting its purpose and parameters improves maintainability.

📝 Suggested JSDoc
+/**
+ * Computes drift between a workflow plan and actual canvas nodes.
+ * Identifies tasks missing from canvas and nodes added outside the plan.
+ * `@param` plan - The workflow plan record to compare against
+ * `@param` canvasNodes - Array of canvas nodes to check
+ * `@returns` CanvasDrift object with drift status and details
+ */
 function computeCanvasDrift(plan: WorkflowPlanRecord, canvasNodes: CanvasNode[]): CanvasDrift {
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/agent-tools/src/copilot/index.ts` around lines 38 - 91, Add a JSDoc
comment for the computeCanvasDrift function describing its purpose, parameters,
and return value: explain that computeCanvasDrift compares a WorkflowPlanRecord
(plan) and an array of CanvasNode (canvasNodes) to detect drift, document the
plan parameter and its tasks, the canvasNodes parameter and that only
'skillResponse' nodes are considered, and describe the CanvasDrift return shape
(hasDrift, planTaskCount, canvasWorkflowNodeCount, missingFromCanvas,
addedOnCanvas, summary). Place the JSDoc immediately above the
computeCanvasDrift declaration and reference the WorkflowPlanRecord, CanvasNode
and CanvasDrift types in the tags (`@param`, `@returns`) and include short notes
about how taskId is extracted from node.data?.metadata.

271-273: Empty catch block silently swallows errors.

While the comment indicates this is non-critical, silently swallowing errors can make debugging difficult. Consider adding minimal logging or at least a more descriptive comment about what errors are expected.

🛡️ Suggested improvement
         } catch {
-          // Non-critical: proceed with patch even if drift check fails
+          // Non-critical: drift check is best-effort; proceed with patch if canvas
+          // data fetch fails (e.g., network issues, permissions). The patch operation
+          // itself will validate task existence on the server side.
         }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/agent-tools/src/copilot/index.ts` around lines 271 - 273, The empty
catch following the drift check silently swallows errors; change it to catch the
error (e.g., catch (err)) and emit a minimal log with context (use the existing
logger such as processLogger or console.warn/console.error) like "Drift check
failed, proceeding with patch" plus the error details, and update the comment to
note which error types are expected to be ignored; update the catch block in the
try/catch around the drift check in packages/agent-tools/src/copilot/index.ts
accordingly.

262-270: Error message exceeds 100 character line length limit.

Per coding guidelines, maximum line length is 100 characters. Consider breaking this into a multi-line template literal or constructing the message with a variable.

✏️ Suggested fix
           if (staleTaskOps.length > 0) {
+            const missingIds = staleTaskOps.map((op) => op.taskId).join(', ');
+            const errorMsg = `Cannot patch: ${staleTaskOps.length} targeted task(s) no longer ` +
+              `exist on canvas. Missing task IDs: ${missingIds}. The canvas may have been ` +
+              'manually modified. Call get_workflow_summary to see drift details, or use ' +
+              'generate_workflow to recreate.';
             return {
               status: 'error',
               data: {
-                error: `Cannot patch: ${staleTaskOps.length} targeted task(s) no longer exist on canvas. Missing task IDs: ${staleTaskOps.map((op) => op.taskId).join(', ')}. The canvas may have been manually modified. Call get_workflow_summary to see drift details, or use generate_workflow to recreate.`,
+                error: errorMsg,
               },
               summary: 'Patch failed due to canvas drift',
             };
           }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/agent-tools/src/copilot/index.ts` around lines 262 - 270, The long
error string in the return block that checks staleTaskOps exceeds the 100-char
line length limit; refactor the message construction inside the same block
(where staleTaskOps is referenced) by assigning the detailed message to a local
variable or using a multi-line template literal (e.g., const errorMsg = `...`)
and then return that variable in data.error, keeping each source code line under
100 characters; ensure you still include the staleTaskOps.map(...).join(', ')
output and preserve the existing status and summary fields.
packages/skill-template/src/prompts/templates/copilot-agent-system.md (1)

45-45: Consider varying sentence structure for better readability.

The static analysis tool flagged three successive sentences beginning with "Use". Consider rephrasing to improve flow:

✏️ Suggested rewording
-**Default Preference**: Use `patch_workflow` when an existing workflow plan exists and user requests specific modifications. Use `generate_workflow` for new workflows or major restructuring. Use `get_workflow_summary` when you need to verify task/variable IDs before making changes. Use `get_canvas_snapshot` when `get_workflow_summary` returns no plan, when drift is detected (`canvasDrift.hasDrift` is true), or when you need to see the actual canvas state.
+**Default Preference**: Prefer `patch_workflow` when an existing workflow plan exists and the user requests specific modifications. For new workflows or major restructuring, call `generate_workflow`. To verify task/variable IDs before making changes, invoke `get_workflow_summary`. Finally, call `get_canvas_snapshot` when `get_workflow_summary` returns no plan, when drift is detected (`canvasDrift.hasDrift` is true), or when you need to see the actual canvas state.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/skill-template/src/prompts/templates/copilot-agent-system.md` at
line 45, Rewrite the "**Default Preference**" sentence to avoid three
consecutive sentences starting with "Use": consolidate and vary sentence
starters so it reads smoothly while keeping the same rules (prefer
patch_workflow for edits to existing plans, generate_workflow for new or major
restructures, get_workflow_summary to verify IDs before changes, and
get_canvas_snapshot when get_workflow_summary returns no plan or
canvasDrift.hasDrift is true). Update the text under the Default Preference
heading in copilot-agent-system.md to merge some clauses and swap sentence
openings (e.g., "Prefer", "Reserve", "Run", "Fallback to") so the guidance is
clearer and the original intent and keywords (patch_workflow, generate_workflow,
get_workflow_summary, get_canvas_snapshot, canvasDrift.hasDrift) remain
unchanged.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@packages/agent-tools/src/copilot/index.ts`:
- Around line 44-52: Extract the repeated taskId extraction into a small helper
(e.g., getNodeTaskId) and replace the inline casts in places that read taskId
from a node (examples: the loop over canvasNodes that sets canvasTaskIds, other
occurrences at the locations currently using the same pattern) with calls to
this helper; ensure the helper signature returns string | undefined and
internally does the safe optional chaining and cast ((node.data?.metadata as
Record<string, unknown>)?.taskId as string | undefined) so callers like
canvasTaskIds.set(taskId, node) and any conditional checks use the helper
instead of duplicating the expression.
- Around line 38-91: Add a JSDoc comment for the computeCanvasDrift function
describing its purpose, parameters, and return value: explain that
computeCanvasDrift compares a WorkflowPlanRecord (plan) and an array of
CanvasNode (canvasNodes) to detect drift, document the plan parameter and its
tasks, the canvasNodes parameter and that only 'skillResponse' nodes are
considered, and describe the CanvasDrift return shape (hasDrift, planTaskCount,
canvasWorkflowNodeCount, missingFromCanvas, addedOnCanvas, summary). Place the
JSDoc immediately above the computeCanvasDrift declaration and reference the
WorkflowPlanRecord, CanvasNode and CanvasDrift types in the tags (`@param`,
`@returns`) and include short notes about how taskId is extracted from
node.data?.metadata.
- Around line 271-273: The empty catch following the drift check silently
swallows errors; change it to catch the error (e.g., catch (err)) and emit a
minimal log with context (use the existing logger such as processLogger or
console.warn/console.error) like "Drift check failed, proceeding with patch"
plus the error details, and update the comment to note which error types are
expected to be ignored; update the catch block in the try/catch around the drift
check in packages/agent-tools/src/copilot/index.ts accordingly.
- Around line 262-270: The long error string in the return block that checks
staleTaskOps exceeds the 100-char line length limit; refactor the message
construction inside the same block (where staleTaskOps is referenced) by
assigning the detailed message to a local variable or using a multi-line
template literal (e.g., const errorMsg = `...`) and then return that variable in
data.error, keeping each source code line under 100 characters; ensure you still
include the staleTaskOps.map(...).join(', ') output and preserve the existing
status and summary fields.

In `@packages/skill-template/src/prompts/templates/copilot-agent-system.md`:
- Line 45: Rewrite the "**Default Preference**" sentence to avoid three
consecutive sentences starting with "Use": consolidate and vary sentence
starters so it reads smoothly while keeping the same rules (prefer
patch_workflow for edits to existing plans, generate_workflow for new or major
restructures, get_workflow_summary to verify IDs before changes, and
get_canvas_snapshot when get_workflow_summary returns no plan or
canvasDrift.hasDrift is true). Update the text under the Default Preference
heading in copilot-agent-system.md to merge some clauses and swap sentence
openings (e.g., "Prefer", "Reserve", "Run", "Fallback to") so the guidance is
clearer and the original intent and keywords (patch_workflow, generate_workflow,
get_workflow_summary, get_canvas_snapshot, canvasDrift.hasDrift) remain
unchanged.

ℹ️ Review info

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 876905c and 2208691.

📒 Files selected for processing (2)
  • packages/agent-tools/src/copilot/index.ts
  • packages/skill-template/src/prompts/templates/copilot-agent-system.md

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