Repository navigation
fix(flows): canvas re-sync after save + Fix with agent on failed runs (B21/B22) - #4823
Conversation
… sidebar/approval card (B21/B22) B21: `flows_update` re-validates/normalizes the saved graph server-side, so the canonical response can differ from what the client sent — but the canvas re-baselined against its own pre-save nodes/edges and ignored the response, so the canonical graph only ever appeared after a navigate-away- and-back remount refetched it. `FlowCanvasPage`'s `handleSave` now re-syncs `draftGraph`/`persistedGraphRef` from the `flows_update` response and bumps `canvasVersion` to re-seed the editable canvas immediately. B22: the failed-run "Fix with agent" repair action (already fully wired from `FlowsPage`'s run-history drawer, Phase 5c) was missing from two other failed-run surfaces that mount `FlowRunInspectorDrawer` without the `onFixWithAgent` prop: `FlowRunsSidebar` (the canvas's own projected run history — the single place fixing in-place is most natural) and `FlowApprovalCard` (the pending-approval notification card, which can render from anywhere in the app). Both now navigate to the flow's canvas seeded with `location.state.copilotRepair`, same mechanism as the existing path. Since `FlowRunsSidebar` is only ever mounted on the SAME flow's canvas page, its repair navigation doesn't remount `FlowCanvasPage`/`FlowEditor` — so the one-time `useState` copilot-open initializer and the copilot panel's once-per-mount repair auto-fire guard wouldn't otherwise re-trigger. Fixed by reacting to a changed repair seed during render (no extra render pass) and folding `location.key` into the copilot panel's key so a same-route repair request still forces a fresh panel mount.
📝 WalkthroughWalkthroughFlow run surfaces now expose agent repair navigation with failing-run metadata. The flow canvas reopens copilot repair seeds across route updates and synchronizes persisted drafts with canonical server responses after saving. ChangesAgent repair flow
Canonical canvas save synchronization
Estimated code review effort: 3 (Moderate) | ~25 minutes Possibly related PRs
Suggested labels: Poem
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
Comment |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 3897e04408
ℹ️ About Codex in GitHub
Codex has been enabled to automatically 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 👍.
When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".
| setDraftGraph(persisted); | ||
| setCanvasVersion(v => v + 1); |
There was a problem hiding this comment.
Avoid overwriting edits made while a save is in flight
When a save is slow and the user keeps editing after clicking Save, EditableFlowCanvas still accepts palette/node/config edits while only Save/Discard are disabled. Those edits update the host draft via onGraphChange, but this response handler later unconditionally replaces the draft with the graph for the older save request and bumps canvasVersion, remounting the canvas and dropping the post-click edits. Please either disable editing during saving or only re-sync from the response if the current draft still matches the saved snapshot.
Useful? React with 👍 / 👎.
There was a problem hiding this comment.
🧹 Nitpick comments (1)
app/src/pages/FlowCanvasPage.tsx (1)
220-232: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winAdd a diagnostic log for the repair-seed reopen transition.
This state transition (a new repair seed reopening the copilot panel without a
FlowEditorremount) has no log line, unlike the other new B22/B21 flows in this file (handleFixWithAgent,handleSave) which all log their transitions.As per path instructions: "New or changed flows must include verbose, grep-friendly diagnostics for entry/exit, branches, external calls, retries/timeouts, state transitions, and errors."
🔍 Proposed diagnostic addition
const [seenCopilotSeed, setSeenCopilotSeed] = useState(initialCopilotSeed); if (initialCopilotSeed !== seenCopilotSeed) { setSeenCopilotSeed(initialCopilotSeed); - if (initialCopilotSeed) setCopilotOpen(true); + if (initialCopilotSeed) { + log('repair seed changed — reopening copilot panel run=%s', initialCopilotSeed.runId); + setCopilotOpen(true); + } }🤖 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 `@app/src/pages/FlowCanvasPage.tsx` around lines 220 - 232, In the render-time transition guarded by initialCopilotSeed !== seenCopilotSeed, add a verbose, grep-friendly diagnostic log when a new repair seed is detected and the copilot panel is reopened. Include the seed transition context and retain the existing setSeenCopilotSeed and setCopilotOpen behavior unchanged.Source: Path instructions
🤖 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.
Nitpick comments:
In `@app/src/pages/FlowCanvasPage.tsx`:
- Around line 220-232: In the render-time transition guarded by
initialCopilotSeed !== seenCopilotSeed, add a verbose, grep-friendly diagnostic
log when a new repair seed is detected and the copilot panel is reopened.
Include the seed transition context and retain the existing setSeenCopilotSeed
and setCopilotOpen behavior unchanged.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
Run ID: 4a049700-f54e-4021-be31-827f01f17718
📒 Files selected for processing (6)
app/src/components/flows/FlowRunsSidebar.test.tsxapp/src/components/flows/FlowRunsSidebar.tsxapp/src/components/notifications/FlowApprovalCard.test.tsxapp/src/components/notifications/FlowApprovalCard.tsxapp/src/pages/FlowCanvasPage.tsxapp/src/pages/__tests__/FlowCanvasPage.test.tsx
Summary
FlowCanvasPage's Save handler now re-syncs the canvas draft from theflows_updateresponse (instead of discarding it) and bumps the canvas key, so the canvas reflects the server's canonical persisted graph immediately — no more navigate-away-and-back to see it.FlowsPage's run-history drawer) into two failed-run surfaces that were missing it:FlowRunsSidebar(the canvas's own projected run history) andFlowApprovalCard(the pending-approval notification card).location.state.copilotRepairnavigation contract;FlowRunsSidebar's case required extra handling since it triggers from the same/flows/:idpage (no page remount), detailed in Solution below.Problem
Tracked live in
my_docs/flows_workflow_bugs.md(bugs B21/B22), found while testing a real Gmail-summary workflow:save_workflow: persisted node_count=N), but the canvas didn't refetch/re-render on save success. Root cause:flows_updatere-validates/normalizes the graph server-side before persisting (schema migration, id defaults, etc.), so the canonical response can differ from the client's just-sent graph — butFlowCanvasPage.handleSavediscarded theupdateFlow(...)response and re-baselined against its own pre-savenodes/edges, so the canonical shape only ever surfaced after a full navigate-away-and-back remount refetched it viaflows_get.FlowRepairRequest,repair-mode builder turn) was already fully built and tested forFlowsPage's run-history drawer (Phase 5c), butFlowRunInspectorDrawer'sonFixWithAgentprop is optional and two other call sites never passed it:FlowRunsSidebar(FlowCanvasPage's own projected sidebar — the single place fixing in-place is most natural) andFlowApprovalCard(aCoreNotificationsurface that can render from anywhere in the app).Solution
FlowCanvasPage.handleSavenow capturesupdateFlow's response, casts itsgraphtoWorkflowGraph, sets it as the newpersistedGraphRef/draftGraph, and bumpscanvasVersion— reusing the same force-remount idiom this file already uses for copilot-proposal accept/reject, so the editable canvas re-seeds from the canonical persisted graph without any navigation.FlowRunsSidebarandFlowApprovalCardnow build ahandleFixWithAgentthat navigates to/flows/:flowIdwithlocation.state.copilotRepair— the same contractFlowsPage's drawer already uses.FlowApprovalCardcan render from anywhere, so this is a plain navigation (mirrorsFlowsPage's existing handler exactly).FlowRunsSidebaris only ever mounted on the SAME flow's canvas page, so this navigation does not remountFlowCanvasPage/FlowEditor(same route, onlylocation.state/location.keychange). That breaks two mount-once assumptions: the copilot-openuseStateinitializer, andWorkflowCopilotPanel's once-per-mount repair auto-fire guard (repairSentRef). Fixed by (a) re-assertingcopilotOpenduring render when a new repair seed prop arrives (React's "adjust state during render" pattern — avoids an extrauseEffectrender pass and thereact-hooks/set-state-in-effectlint warning), and (b) foldinglocation.key(react-router mints a fresh one per navigation, including same-path repeats) into the copilot panel'skeyso a same-route repair request still forces a fresh panel mount. Build-seed/manual-open behavor is untouched — those already get a freshFlowEditormount today, so their panel key stays the stable'copilot'string.Testing
FlowCanvasPage.test.tsxcase: mocksupdateFlowto resolve with a graph that differs from what the client sent (renamed node + an extra "server-added" node) and asserts the canvas shows all three response nodes immediately, with no extraflows_getcall and no remount.FlowRunsSidebar.test.tsx(new file): run list renders, opens the inspector drawer, forwardsonFixWithAgent, and — invoking that callback — asserts the sidebar's own drawer closes and the app navigates to the same flow with the rightcopilotRepairstate.FlowApprovalCard.test.tsxwith the equivalentonFixWithAgent-wiring and navigation assertions (also switched its render helper to aMemoryRouter, now required since the card callsuseNavigate).pnpm typecheck,pnpm lint,pnpm i18n:checkall pass; no new user-facing strings were introduced (no i18n key changes needed).src/components/flows,src/components/notifications, andsrc/pages/__tests__/FlowCanvasPage.test.tsx: 23 files / 217 tests passing.Impact
app/src/); no Rust/RPC changes. No new external network dependencies.flows_update,flows_build mode=repair) and the existingcopilotRepairnavigation contract.Related
my_docs/flows_workflow_bugs.mdas B21/B22, not a GitHub issue)FlowListRow.tsx'slast_statustext andFlowRunsDrawer's per-row status badges still require opening the inspector drawer before "Fix with agent" is reachable (one extra click, not a missing wire) — left out of scope here.AI Authored PR Metadata (required for Codex/Linear PRs)
Linear Issue
Commit & Branch
Validation Run
pnpm --filter openhuman-app format:checkpnpm typechecksrc/components/flows,src/components/notifications,src/pages/__tests__/FlowCanvasPage.test.tsx(217 passing)Validation Blocked
command:N/Aerror:N/Aimpact:N/ABehavior Changes
Parity Contract
WorkflowCopilotPanel's repair/build auto-fire guards (repairSentRef/buildSentRef) are untouched; only the panel's remount key changed for the new same-route repair case.Duplicate / Superseded PR Handling
Summary by CodeRabbit
New Features
Bug Fixes