Unifies the previously mobile-only Oversight overflow menu into a single, always-present dropdown that replaces the scattered desktop oversight buttons and the separate mobile affordance.
- Replace discrete desktop oversight action buttons in TaskDetailModal's footer with one universal "Oversight actions" dropdown trigger, reusing the menu across desktop and mobile breakpoints.
- Simplify TaskDetailModal.tsx footer rendering logic, removing now-redundant responsive branching for oversight controls.
- Update TaskDetailModal.css to drop the old mobile-only oversight-overflow styles and support the unified dropdown across breakpoints.
- Update definition-actions, oversight-controls, oversight-mobile, rendering, and responsive-and-dependencies tests to assert the single dropdown behavior and disambiguate the exact "Actions" button query from the new "Oversight actions" aria-label.
- Refresh docs/dashboard-guide.md to describe the unified oversight dropdown UX.
Files changed:
docs/dashboard-guide.md | 14 +-
packages/dashboard/app/components/TaskDetailModal.css | 89 +++++------
packages/dashboard/app/components/TaskDetailModal.tsx | 177 +++------------------
packages/dashboard/app/components/__tests__/TaskDetailModal.definition-actions.test.tsx | 70 ++++----
packages/dashboard/app/components/__tests__/TaskDetailModal.oversight-controls.test.tsx | 130 ++++++++++-----
packages/dashboard/app/components/__tests__/TaskDetailModal.oversight-mobile.test.tsx | 42 +++--
packages/dashboard/app/components/__tests__/TaskDetailModal.rendering.test.tsx | 27 ++--
packages/dashboard/app/components/__tests__/TaskDetailModal.responsive-and-dependencies.test.tsx | 57 ++++---
8 files changed, 278 insertions(+), 328 deletions(-)
Fusion-Task-Id: FN-7604
Fusion-Task-Lineage: afbb7573-d654-48db-a9ac-aecbd8e22e46
Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
Docs-only change adding front-door discovery for the already-shipped planner-oversight feature (FN-7508 → FN-7583), which previously had no entry point outside internal reference docs.
- Add a README.md feature table row and a new "Planner oversight" section describing oversight levels (off/observe/steer/autonomous) and the always-on human-confirmation gate for merge/PR and destructive actions, linking to Settings Reference and Dashboard Guide
- Add a README.md capabilities bullet cross-linking the new section
- Add a docs/README.md hub row pointing to Settings Reference, Dashboard Guide, and Architecture for planner oversight, and extend the 'power user' reading path
- Add a one-line pointer in docs/getting-started.md workflow section noting per-task/workflow oversight controls
Files changed:
README.md | 11 +++++++++++
docs/README.md | 9 ++++++---
docs/getting-started.md | 3 +++
3 files changed, 20 insertions(+), 3 deletions(-)
Fusion-Task-Id: FN-7598
Fusion-Task-Lineage: 8141b44c-f007-45c0-a057-f4eeb34ae8d4
Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
Reorders task-definition prompt templates so the Before -> After Transformation section appears before other sections, making the expected change visible first.
- Move the Before -> After Transformation section ahead of other sections in agent-prompts.ts task-definition templates
- Update docs/task-management.md to reflect the new section order
- Add/extend tests in agent-prompts.test.ts and triage.test.ts covering the new ordering
- Add changeset fn-7593-before-after-top.md documenting the change
Files changed:
.changeset/fn-7593-before-after-top.md | 7 +++++++
docs/task-management.md | 2 +-
packages/core/src/__tests__/agent-prompts.test.ts | 20 ++++++++++++++++++++
packages/core/src/agent-prompts.ts | 20 +++++++++++++-------
packages/engine/src/__tests__/triage.test.ts | 17 +++++++++++++++++
5 files changed, 58 insertions(+), 8 deletions(-)
Fusion-Task-Id: FN-7593
Fusion-Task-Lineage: d0d5eb4d-2fe0-456c-b061-5c078b78911b
Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
Adds a discoverable built-in Brainstorming workflow composing the ask-user + exit-gate reach-out loop ahead of the standard coding plan/execute/review/merge spine, plus a WorkflowNodeEditor fix so clearing the ask-user question textarea deletes the config key instead of persisting an empty string.
- Add packages/core/src/builtin-brainstorming-workflow-ir.ts registering builtin:brainstorming (non-default, default-enabled): ask-user -> refine prompt -> exit-gate-on-approval ahead of the unmodified Coding plan/execute/review/merge spine
- Wire the new builtin into packages/core/src/builtin-workflows.ts and extend the builtin-workflows parity test suite
- Add builtin-brainstorming-workflow-ir.test.ts covering the new workflow's IR shape and validation
- Fix WorkflowNodeEditor.tsx ask-user question textarea onChange to delete the config.question key when cleared to empty (validateAskUserAndExitGateNodes rejects present-but-empty question; only an absent key falls back to the engine default)
- Update docs/workflow-steps.md to document builtin:brainstorming as a selectable built-in composition
- Add .changeset/fn-7584-brainstorming-builtin.md (minor, feature)
Files changed:
.changeset/fn-7584-brainstorming-builtin.md | 7 ++
docs/workflow-steps.md | 2 +-
.../builtin-brainstorming-workflow-ir.test.ts | 79 ++++++++++++++++
.../core/src/__tests__/builtin-workflows.test.ts | 66 +++++++++++++
.../core/src/builtin-brainstorming-workflow-ir.ts | 103 +++++++++++++++++++++
packages/core/src/builtin-workflows.ts | 46 +++++++++
.../app/components/WorkflowNodeEditor.tsx | 18 +++-
7 files changed, 319 insertions(+), 2 deletions(-)
Fusion-Task-Id: FN-7584
Fusion-Task-Lineage: 2c0258c2-9a35-403a-8688-ee49393a7231
Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
Add workflow nodes for mid-flow user reach-out and early exit from a workflow run.
- Add `ask-user` IR node kind that reuses the await-input park/resume mechanism and surfaces the question in the task chat for brainstorming/clarification.
- Add `exit-gate` IR node kind that terminates the workflow early, with an optional condition.
- Wire both node kinds through the engine executor and workflow-node-handlers, including a new exit-gate-runner.
- Update the WorkflowNodeEditor palette, node summaries, and node help text for the two new node types.
- Extend workflow-flow-mapping to support the new node kinds.
- Keep `prompt`+`awaitInput` as a back-compat alias.
- Add core/engine/dashboard tests covering the new node kinds.
- Document the new nodes in docs/workflow-steps.md.
- Add changeset for the new minor feature.
Files changed:
.changeset/fn-7579-ask-user-exit-gate-nodes.md | 7 +
docs/workflow-steps.md | 28 ++++
packages/core/src/__tests__/workflow-ir.test.ts | 120 ++++++++++++++
packages/core/src/workflow-ir-types.ts | 12 +-
packages/core/src/workflow-ir.ts | 47 ++++++
.../app/components/WorkflowNodeEditor.tsx | 181 ++++++++++++++++++++-
.../app/components/__tests__/node-summary.test.ts | 43 +++++
.../__tests__/workflow-flow-mapping.test.ts | 49 ++++++
.../app/components/nodes/WorkflowNodeTypes.tsx | 14 +-
.../dashboard/app/components/nodes/node-help.ts | 24 +++
.../dashboard/app/components/nodes/node-summary.ts | 28 ++++
.../app/components/workflow-flow-mapping.ts | 4 +
.../workflow-graph-executor-handlers.test.ts | 115 +++++++++++++
.../src/__tests__/workflow-node-handlers.test.ts | 66 ++++++++
packages/engine/src/executor.ts | 23 ++-
packages/engine/src/workflow-node-handlers.ts | 18 +-
.../src/workflow-node-runners/exit-gate-runner.ts | 81 +++++++++
17 files changed, 849 insertions(+), 11 deletions(-)
Fusion-Task-Id: FN-7579
Fusion-Task-Lineage: 9a89ff49-200d-4a6c-b97c-15d219349ee5
Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
Extends FN-7554's single-repo PR revert path to workspace (multi-repo) tasks: when autoMerge is disabled, the revert route now opens one dedicated fusion/revert-<id> PR per sub-repo instead of refusing workspace tasks outright.
- Add prepareWorkspaceRevertPrBranches (packages/engine/src/task-revert.ts): classifies every sub-repo first and only prepares a per-sub-repo fusion/revert-<id> branch when all sub-repos are clean/already-reverted (all-or-nothing at branch-prep phase); never force-writes any sub-repo integration branch.
- Export the new helper from packages/engine/src/index.ts.
- Extend POST /api/tasks/:id/revert (register-task-workflow-routes.ts) to resolve owner/repo and check the GitHub rate limiter for every sub-repo before pushing/creating any PR, opening one PR per sub-repo and returning an additive { mode: "pr", clean: true, workspace: { repos: [...] } } result; degrades the whole task to needsHuman if GitHub is unconfigured or any sub-repo is rate-limited, rather than opening a partial subset of PRs.
- Leave existing { mode: "git" | "ai" | "pr" } shapes, the autoMerge:true workspace path, and FN-7554's single-repo PR path unchanged.
- Add engine real-git coverage (task-revert-workspace-pr.real-git.test.ts) and extend dashboard route tests (task-revert-route.test.ts) for the new workspace PR path.
- Add changeset (.changeset/fn-7577-workspace-pr-revert.md, minor) and update docs/task-management.md.
Files changed:
.changeset/fn-7577-workspace-pr-revert.md | 7 +
docs/task-management.md | 3 +-
.../src/__tests__/task-revert-route.test.ts | 313 ++++++++++++++++-
.../src/routes/register-task-workflow-routes.ts | 181 +++++++++-
.../task-revert-workspace-pr.real-git.test.ts | 371 +++++++++++++++++++++
packages/engine/src/index.ts | 4 +
packages/engine/src/task-revert.ts | 295 ++++++++++++++++
7 files changed, 1166 insertions(+), 8 deletions(-)
Fusion-Task-Id: FN-7577
Fusion-Task-Lineage: bedbfab7-5804-485f-9b40-64531edfc64a
Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
Extends GitHubIssueCommentService so task-close comments on issues in the runfusion/fusion repo itself append both a current-version and target-next-minor-release line, while comments on all other linked repos stay byte-for-byte unchanged.
- Add isFusionSelfRepo() and computeNextMinorVersion() helpers to github-issue-comment.ts
- Append "Current version: v{current}" and "Target release: v{next-minor}" lines only when the linked source issue's repo is runfusion/fusion (case-insensitive)
- Fall back silently (no version lines) when the resolved version is unparseable or the unresolved 0.0.0 sentinel
- Add changeset (minor) documenting the new behavior
- Update docs/settings-reference.md and docs/gitlab-parity-inventory.md
- Expand github-issue-comment.test.ts coverage for self-repo vs other-repo behavior and version edge cases
Files changed:
.changeset/fn-7575-release-version-comment.md | 7 +
docs/gitlab-parity-inventory.md | 2 +-
docs/settings-reference.md | 2 +-
packages/dashboard/src/__tests__/github-issue-comment.test.ts | 142 ++++++++++++++++++++-
packages/dashboard/src/github-issue-comment.ts | 69 +++++++++-
5 files changed, 212 insertions(+), 10 deletions(-)
Fusion-Task-Id: FN-7575
Fusion-Task-Lineage: b7cf7e6f-8d96-4442-8595-5d54ea911481
Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
Adds a PR-based revert path for done/archived tasks in autoMerge:false projects instead of refusing outright.
- New engine export `prepareRevertPrBranch` (packages/engine/src/task-revert.ts) prepares a dedicated `fusion/revert-<id>` branch off the base branch's HEAD and applies the revert commit(s) there, never mutating the base branch itself.
- `POST /api/tasks/:id/revert` route gains an additive `{ mode: "pr", clean: true, prUrl, prNumber, revertBranch, existingPr? }` result for clean single-repo reverts under autoMerge:false, reusing GitHubClient.createPr, findPrForBranch idempotency, and the manual:true PR handoff.
- Existing `{ mode: "git" | "ai", ... }` result shapes and the autoMerge:true path are unchanged.
- Workspace (multi-repo) tasks are explicitly refused for PR-based revert (out of scope; single PR cannot represent a multi-repo revert).
- Adds real-git integration tests for the new branch-prep/apply/commit flow and expands the dashboard route test coverage.
- Adds a changeset for @runfusion/fusion (minor) and a small task-management doc update.
Files changed:
.changeset/fn-7554-pr-based-revert.md | 7 +
docs/task-management.md | 2 +-
.../src/__tests__/task-revert-route.test.ts | 196 +++++++++++++++++++-
.../src/routes/register-task-workflow-routes.ts | 182 ++++++++++++++++++
.../src/__tests__/task-revert-pr.real-git.test.ts | 206 +++++++++++++++++++++
packages/engine/src/index.ts | 3 +
packages/engine/src/task-revert.ts | 154 +++++++++++++++
7 files changed, 747 insertions(+), 3 deletions(-)
Fusion-Task-Id: FN-7554
Fusion-Task-Lineage: 9b1bfd82-2428-4cf7-9b36-77afe3517a14
Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
Unifies OAuth expiry detection so expired Claude subscription logins correctly show as disconnected with a re-login prompt, and adds a proactive engine-side scheduler that refreshes tokens before they expire.
- Share expiry-detection logic between OAuthExpiryMonitor and the /api/auth/status route so both agree on when a token is expired.
- Add engine-side oauth-refresh-scheduler that proactively refreshes OAuth tokens ahead of expiry, wired into project-engine (guarded by skipNotifier).
- Extend auth-storage with the helpers needed for expiry checks/refresh.
- Add tests covering routes-auth status detection, auth-storage expiry helpers, and the new refresh scheduler.
- Document the new behavior in dashboard-guide.md and settings-reference.md.
- Add changeset for the user-facing fix.
Files changed:
.../fn-7574-oauth-expiry-detection-refresh.md | 7 +
docs/dashboard-guide.md | 4 +
docs/settings-reference.md | 4 +
.../dashboard/src/__tests__/routes-auth.test.ts | 76 +++++++++++
.../dashboard/src/routes/register-auth-routes.ts | 25 +++-
packages/engine/src/__tests__/auth-storage.test.ts | 60 +++++++++
packages/engine/src/auth-storage.ts | 14 +-
.../__tests__/oauth-refresh-scheduler.test.ts | 141 ++++++++++++++++++++
packages/engine/src/notification/index.ts | 3 +
.../src/notification/oauth-refresh-scheduler.ts | 143 +++++++++++++++++++++
packages/engine/src/project-engine.ts | 14 +-
11 files changed, 488 insertions(+), 3 deletions(-)
Fusion-Task-Id: FN-7574
Fusion-Task-Lineage: 59996eac-c070-4992-9727-d066c6934b69
Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
Adds a client-side helper and UI affordances so AI-undo tasks and their source tasks link to each other in the dashboard, without any new API or backend changes.
- Add packages/dashboard/app/utils/taskRevert.ts with getRevertOfId() (resolves the revertOf marker written by createAiUndoTask, with a defensive sourceType==="recovery" fallback to sourceParentTaskId) and findOpenUndoTaskForSource() (reverse lookup for the most recent open undo task pointing at a source task)
- TaskCard and TaskDetailModal now render "Undo of <id>" / "Undo task: <id>" provenance links using the shared helper, keeping forward and reverse affordances consistent
- Add TaskCard.css styling for the new undo-link affordance
- Add/extend tests: TaskCard.test.tsx and TaskDetailModal.rendering.test.tsx cover the new undo/source link rendering
- Update docs/task-management.md and packages/i18n/locales/en/app.json for the new UI copy
Files changed:
docs/task-management.md | 2 +-
packages/dashboard/app/components/TaskCard.css | 37 +++++
packages/dashboard/app/components/TaskCard.tsx | 29 +++-
.../dashboard/app/components/TaskDetailModal.tsx | 46 ++++++
.../app/components/__tests__/TaskCard.test.tsx | 46 ++++++
.../__tests__/TaskDetailModal.rendering.test.tsx | 160 +++++++++++++++++++++
packages/dashboard/app/utils/taskRevert.ts | 80 +++++++++++
packages/i18n/locales/en/app.json | 7 +-
8 files changed, 403 insertions(+), 4 deletions(-)
Fusion-Task-Id: FN-7555
Fusion-Task-Lineage: c3d2dbd2-4d86-40b5-afce-0ca7a6dfc73d
Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
Fixes recurrence #4 of the mobile terminal excess character-spacing bug: SessionTerminal and TerminalModal now force a second genuine xterm font remeasure AFTER fitAddon.fit() settles the post-fit column count, since handleResize() never re-bakes DomRenderer's letter-spacing compensation itself.
- SessionTerminal.tsx: call forceTerminalFontRemeasure() again after fit()/sendResizeMessage() in the resize handler, re-baking spacing against the settled (post-fit) column count instead of the stale pre-fit one.
- TerminalModal.tsx: same second forceTerminalFontRemeasure() call after fitAddon.fit()/sendResize() in its resize handling path.
- Expanded SessionTerminal.test.tsx and TerminalModal.test.tsx coverage to assert the post-fit remeasure occurs.
- Added docs/solutions/ui-bugs/xterm-options-noop-remeasure-after-font-settle.md documenting recurrence #4 root cause (DomRenderer._setDefaultSpacing() never recomputes from handleResize()).
- Added changeset fn-7567-mobile-terminal-spacing.md (patch, category fix).
Files changed:
.changeset/fn-7567-mobile-terminal-spacing.md | 7 +
docs/solutions/ui-bugs/xterm-options-noop-remeasure-after-font-settle.md | 89 ++++++
packages/dashboard/app/components/SessionTerminal.tsx | 23 ++
packages/dashboard/app/components/TerminalModal.tsx | 43 ++-
packages/dashboard/app/components/__tests__/SessionTerminal.test.tsx | 127 +++++++-
packages/dashboard/app/components/__tests__/TerminalModal.test.tsx | 334 ++++++++++++++++++++-
6 files changed, 619 insertions(+), 4 deletions(-)
Fusion-Task-Id: FN-7567
Fusion-Task-Lineage: 5da20522-82d3-4c4d-9008-db71bc5b4d75
Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
Manual plan approval now skips re-asking for approval when a re-specification produces an identical plan to one already approved.
- Add nullable Task.approvedPlanFingerprint field with DB migration 139 to track the approved PROMPT.md fingerprint
- Skip re-parking at awaiting-approval when replan/plan-review-retry/self-healing rebound yields the same plan fingerprint as before
- Require fresh approval when the plan content changes or when a plan is rejected
- Leave Release Authorization, Workflow Plan Review, and auto-approve-all behavior unchanged
- Add/extend tests across core (db, plan-approval, store-persistence), engine (triage), and dashboard (routes-github) to cover fingerprint comparison and idempotent re-approval
- Update docs (settings-reference.md, workflow-steps.md) to describe the idempotent approval behavior
- Add changeset for @runfusion/fusion (patch)
Files changed:
.changeset/fn-7569-plan-approval-idempotent.md | 7 +
docs/settings-reference.md | 2 +-
docs/workflow-steps.md | 2 +
packages/core/src/__tests__/db.test.ts | 54 +++++++
packages/core/src/__tests__/plan-approval.test.ts | 31 +++-
.../core/src/__tests__/store-persistence.test.ts | 39 +++++
packages/core/src/db.ts | 22 ++-
packages/core/src/index.ts | 2 +-
packages/core/src/plan-approval.ts | 23 +++
packages/core/src/store.ts | 20 ++-
packages/core/src/types.ts | 13 ++
.../dashboard/src/__tests__/routes-github.test.ts | 69 +++++++-
.../src/routes/register-task-workflow-routes.ts | 37 ++++-
packages/engine/src/__tests__/triage.test.ts | 178 ++++++++++++++++++++-
packages/engine/src/triage.ts | 58 +++++--
15 files changed, 527 insertions(+), 30 deletions(-)
Fusion-Task-Id: FN-7569
Fusion-Task-Lineage: 7d3855ae-6f45-4571-90db-cf1ae3b541dd
Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
Relocates the FN-7519 Intervention Timeline out of the inline oversight cluster and into the task Activity view as a fourth selectable segment.
- Add `interventions` to the `ActivitySegment` union and gate its dropdown option on the same oversight-active expression previously used by the inline mount
- Render `PlannerInterventionTimeline` in a new Activity segment panel instead of inline next to the oversight controls
- Fall back to the Live segment automatically if oversight turns off (or was never resolved) while Interventions is selected, avoiding an orphaned selection
- Update oversight-controls and task-activity-chat tests to cover the new dropdown segment and removed inline mount
- Add changeset and update dashboard guide docs
Files changed:
...7571-intervention-timeline-activity-dropdown.md | 7 +
docs/dashboard-guide.md | 4 +-
.../dashboard/app/components/TaskDetailModal.tsx | 49 ++++---
.../TaskDetailModal.oversight-controls.test.tsx | 148 +++++++++++++++++++++
.../TaskDetailModal.task-activity-chat.test.tsx | 7 +-
5 files changed, 195 insertions(+), 20 deletions(-)
Fusion-Task-Id: FN-7571
Fusion-Task-Lineage: 331a1b32-5712-4583-9777-e36ff5cc3ffc
Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
Adds all-or-nothing git-revert support for multi-repo workspace tasks and wires it into the existing revert route/AI-undo/per-sha machinery.
- Add `resolveWorkspaceTaskRevertCommits` and `revertWorkspaceTask` to `packages/engine/src/task-revert.ts`, dry-run classifying every sub-repo first and only committing per-repo revert commits when every sub-repo is clean/already-reverted; any conflicting sub-repo rolls back every already-committed sub-repo.
- Extract shared `applyAndCommitRevert" apply/commit machinery (built on the existing `applyRevertNoCommit` primitive) so the workspace path reuses the same commit-message/trailer contract as the single-repo path.
- Add a defensive `isWorkspaceTask` guard to `performTaskRevert` so workspace tasks can never be silently reverted through the single-repo path.
- Wire `POST /api/tasks/:id/revert` (register-task-workflow-routes.ts) to dispatch workspace tasks to `revertWorkspaceTask`, preserving the existing `mode` (git/ai/auto) and AI-undo-fallback contract for workspace conflicts.
- Export the new workspace revert types/functions from `packages/engine/src/index.ts`.
- Add route-dispatch and real-git workspace revert test coverage; update docs and add a changeset.
Files changed:
.changeset/fn-7547-workspace-task-revert.md | 7 +
docs/task-management.md | 8 +-
packages/dashboard/src/__tests__/task-revert-route.test.ts | 102 +++++
packages/dashboard/src/routes/register-task-workflow-routes.ts | 76 +++-
packages/engine/src/__tests__/task-revert.workspace.real-git.test.ts | 272 +++++++++++++
packages/engine/src/index.ts | 6 +
packages/engine/src/task-revert.ts | 448 ++++++++++++++++++++-
7 files changed, 897 insertions(+), 22 deletions(-)
Fusion-Task-Id: FN-7547
Fusion-Task-Lineage: b1b5eeda-06fd-43c1-8163-74b62c77b000
Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
Root-cause fix for xterm text rendering with wide inter-character gaps on mobile after fonts settle post-load.
- Added forceTerminalFontRemeasure() in terminalPreferences.ts to work around xterm's OptionsService setter being a no-op when reassigning an already-current fontFamily/fontSize
- Applied the remeasure helper at every post-waitForTerminalFontMetrics() settle site in TerminalModal.tsx and SessionTerminal.tsx
- Added regression tests covering the remeasure invariant across SessionTerminal, TerminalModal, and terminalPreferences
- Documented root cause and fix in docs/solutions/ui-bugs/xterm-options-noop-remeasure-after-font-settle.md
- Added changeset for @runfusion/fusion (patch)
Files changed:
.changeset/fn-7561-mobile-terminal-spacing.md | 7 +
docs/solutions/ui-bugs/xterm-options-noop-remeasure-after-font-settle.md | 120 ++++++++++
packages/dashboard/app/components/SessionTerminal.tsx | 11 +-
packages/dashboard/app/components/TerminalModal.tsx | 11 +-
packages/dashboard/app/components/__tests__/SessionTerminal.test.tsx | 108 ++++++++-
packages/dashboard/app/components/__tests__/TerminalModal.test.tsx | 257 ++++++++++++++++++++-
packages/dashboard/app/utils/__tests__/terminalPreferences.test.ts | 55 +++++
packages/dashboard/app/utils/terminalPreferences.ts | 38 +++
8 files changed, 601 insertions(+), 6 deletions(-)
Fusion-Task-Id: FN-7561
Fusion-Task-Lineage: ad9da396-abf4-46ea-8c60-f2ed40fa4b01
Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
Fixes the mobile oversight overflow menu rendering two overlapping menus by narrowing auto-focus to actionable button menuitems.
- Change the menu-open auto-focus effect to query `button.detail-oversight-menu-item` instead of matching any `.detail-oversight-menu-item` element, since the native level `<select>` also carried that class and was matched first.
- Programmatically focusing the native `<select>` triggered its OS option picker, which rendered as a second overlapping menu on top of the custom `role="menu"` popover.
- Add regression tests covering: auto-focus landing on the first button menuitem when nudge/stop/explain are available, no fallback focus on the select when oversight is off (level-only state), and confirming the desktop inline select is unaffected.
- Update dashboard-guide.md docs to describe the fixed auto-focus behavior.
Files changed:
docs/dashboard-guide.md | 2 +-
packages/dashboard/app/components/TaskDetailModal.tsx | 3 +-
.../app/components/__tests__/TaskDetailModal.oversight-mobile.test.tsx | 96 ++++++++++++++++++++++
3 files changed, 99 insertions(+), 2 deletions(-)
Fusion-Task-Id: FN-7562
Fusion-Task-Lineage: f39068f1-811f-4663-ade1-ff020a389ae5
Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
Disambiguate release-authorization approval holds from manual plan-approval holds so auto-approve no longer appears broken.
- Add `Task.awaitingApprovalReason` (`"release-authorization" | null`) to distinguish the release-authorization gate from the independent manual plan-approval gate, both of which set `status: "awaiting-approval"`.
- Stamp `awaitingApprovalReason: "release-authorization"` when the release gate blocks a task, and explicitly clear it (`null`) when the manual plan-approval gate parks the task, so a stale reason never survives a replan.
- Add DB migration/persistence support for the new column in `db.ts`/`store.ts`/`types.ts`.
- TaskCard/TaskDetailModal now render a distinct status for release-authorization holds and suppress the generic manual Approve/Reject affordance for them.
- Add i18n string and docs updates (`settings-reference.md`, `workflow-steps.md`) plus a changeset.
- Extend regression tests in db, triage, TaskCard, and TaskDetailModal to cover the new reason field and disambiguated UI.
Files changed:
$(git diff --cached --stat)
Fusion-Task-Id: FN-7559
Fusion-Task-Lineage: 0b37cbf0-40a4-4165-8088-482ed365ba19
Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
Adds an opt-in per-sha commit granularity mode to the task-revert git path, alongside its default squash behavior, and merges it cleanly with the existing FN-7524 AI-undo mode support.
- Add `TaskRevertGranularity` ("squash" | "per-sha") and thread an optional `granularity` option through `performTaskRevert`/`PerformTaskRevertOptions`.
- Factor a shared `applyRevertNoCommit` primitive (stage + no-op/conflict detection) used by both the squash and new per-sha apply paths.
- `"per-sha"` creates one attributed `revert(FN-xxxx): ...` commit per original sha (each with its own `Fusion-Task-Id` trailer and audit line), skipping no-op shas without empty commits; a mid-batch conflict rolls the whole batch back to the pre-call HEAD.
- Extend `TaskRevertResult`'s clean shape with `revertCommitShas: string[]` (all created commits) alongside the existing `revertCommitSha`.
- `POST /api/tasks/:id/revert` accepts an optional `granularity` request-body field (default `"squash"`, validated, 400 on unknown values) and forwards it to the engine service; documented alongside the existing `mode` (git/ai/auto) contract.
- Add real-git and route-level test coverage for per-sha creation, no-op skipping, default-squash behavior, and mid-batch conflict rollback.
- Update docs/task-management.md's revert section and add a changeset.
Files changed:
.changeset/fn-7548-per-sha-revert-granularity.md | 7 +
docs/task-management.md | 3 +-
packages/dashboard/src/__tests__/task-revert-route.test.ts | 46 +++++-
packages/dashboard/src/routes/register-task-workflow-routes.ts | 51 ++++--
packages/engine/src/__tests__/task-revert.real-git.test.ts | 124 +++++++++++++++
packages/engine/src/index.ts | 2 +
packages/engine/src/task-revert.ts | 176 +++++++++++++++++----
7 files changed, 359 insertions(+), 50 deletions(-)
Fusion-Task-Id: FN-7548
Fusion-Task-Lineage: b9548f5e-fcc2-45d4-98e0-dd7340928208
Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
Adds an AI-undo fallback to the revert route: when a git-based revert conflicts or is unsupported, an ordinary board task is created to perform the undo via AI instead of a forced/failed git write.
- POST /tasks/:id/revert now accepts an optional `{ mode?: "git" | "ai" | "auto" }` body (default "auto"); unknown values reject with 400.
- "git" preserves the FN-7523 git-only contract unchanged; "ai" always creates the AI-undo task; "auto" tries git first and falls back to AI only on a conflicting or unsupported (e.g. workspace) result — needsHuman (autoMerge:false) never triggers the fallback.
- New engine helpers in task-revert.ts: `createAiUndoTask`, `buildAiUndoTaskDescription`, `REVERT_OF_METADATA_KEY`, plus `AiUndoTaskResult`/`CreateAiUndoTaskDeps` types, exported from packages/engine/src/index.ts.
- The AI-undo task is created via the normal triage-column `store.createTask` path with no dependency on the source task, referencing the source task's mission, id, and landed files, and instructing an undo commit using the `revert(FN-xxxx): ...` convention.
- New core `TaskStore.findOpenRevertTaskForSource` backs an idempotency guard: a repeated call while an AI-undo task is still open returns the same `createdTaskId` with `alreadyOpen: true` instead of creating a duplicate.
- Updated docs/task-management.md's revert section to document the git path + AI-undo fallback contract.
- Added a minor changeset for the @runfusion/fusion release notes.
- Added/extended tests: packages/engine/src/__tests__/task-revert-ai-undo.test.ts (new) and packages/dashboard/src/__tests__/task-revert-route.test.ts (extended) covering mode validation, auto-fallback-on-conflict, forced "ai" mode, and the duplicate-open-task guard.
Files changed:
.changeset/fn-7524-ai-undo-revert.md | 7 +
docs/task-management.md | 13 +-
packages/core/src/store.ts | 31 +++++
packages/dashboard/src/__tests__/task-revert-route.test.ts | 143 ++++++++++++++++++++-
packages/dashboard/src/routes/register-task-workflow-routes.ts | 75 +++++++++--
packages/engine/src/__tests__/task-revert-ai-undo.test.ts | 114 ++++++++++++++++
packages/engine/src/index.ts | 5 +
packages/engine/src/task-revert.ts | 117 ++++++++++++++++-
8 files changed, 487 insertions(+), 18 deletions(-)
Fusion-Task-Id: FN-7524
Fusion-Task-Lineage: 64dfedcf-c286-4c46-8cf8-51ec5e668bf7
Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
Changes the project-wide plan approval default from deferring to per-workflow settings to auto-approving all task plans, so new/unset projects skip the manual awaiting-approval gate by default.
- Change DEFAULT_PROJECT_SETTINGS.planApprovalMode default from "workflow" to "auto-approve-all" in settings-schema.ts, with FNXC comments documenting the requirement change
- Update ProjectSettings.planApprovalMode JSDoc in types.ts to reflect the new default
- Update useAppSettings hook's initial state and hydration fallback to default to "auto-approve-all" while still honoring an explicit stored "workflow" value
- Update MergeSection UI: move the "(default)" label from the "Use workflow setting" option to "Auto-approve all tasks", keeping the select's fallback value in sync
- Update settings-reference.md docs and i18n locale/resource strings to match the new default label
- Update existing tests (MergeSection legacy auto-merge cleanup, settings default descriptions, useAppSettings) to assert the new default, and add coverage for the updated hydration/fallback behavior
- Add changeset fn-7557-plan-auto-approve-default.md documenting the behavior change
Files changed:
.changeset/fn-7557-plan-auto-approve-default.md | 7 +++++
docs/settings-reference.md | 2 +-
packages/core/src/settings-schema.ts | 6 +++-
packages/core/src/types.ts | 3 ++
.../dashboard/app/components/SettingsModal.tsx | 3 +-
.../components/settings/sections/MergeSection.tsx | 9 ++++--
.../MergeSection.legacy-automerge-cleanup.test.tsx | 4 +--
.../settings-default-descriptions.test.tsx | 3 +-
.../app/hooks/__tests__/useAppSettings.test.ts | 35 +++++++++++++++++++---
packages/dashboard/app/hooks/useAppSettings.ts | 12 ++++++--
packages/i18n/locales/en/app.json | 4 +--
packages/i18n/src/resources.d.ts | 2 +-
12 files changed, 71 insertions(+), 19 deletions(-)
Fusion-Task-Id: FN-7557
Fusion-Task-Lineage: 7dcfe339-6088-4ebc-8387-eb81258a693d
Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
Add a visible group label and an in-DOM disabled-reason helper to the task-detail oversight controls so the Nudge/Explain cluster is no longer confusing when greyed out, and make Explain always-openable since it is read-only.
- Add a `detail-oversight-controls-label` group label above the Nudge/Stop/Explain buttons, gated by the same visibility condition as the buttons (mobile and desktop clusters)
- Add a `detail-overseer-nudge-disabled-reason` in-DOM helper line explaining why Nudge is unavailable, instead of relying only on a hover title
- Remove the disabled gate from Explain (it's read-only/non-mutating) and update its title copy to reflect that it always opens and shows last-known state when inactive
- Add regression tests covering the new label/helper text and Explain's always-enabled behavior
- Add a patch changeset and a docs/dashboard-guide.md note
Files changed:
.changeset/FN-7546-oversight-controls-clarity.md | 7 +++
docs/dashboard-guide.md | 2 +
.../dashboard/app/components/TaskDetailModal.css | 42 +++++++++++++
.../dashboard/app/components/TaskDetailModal.tsx | 41 +++++++++++--
.../TaskDetailModal.oversight-controls.test.tsx | 69 ++++++++++++++++++++++
5 files changed, 157 insertions(+), 4 deletions(-)
Fusion-Task-Id: FN-7546
Fusion-Task-Lineage: d1f342ea-2de0-4b54-9930-9b3d540c7af6
Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
Fix agent-created artifacts not showing live in the dashboard because a second TaskStore instance (e.g. engine writing while dashboard polls) never detected or re-emitted artifact:registered for rows it did not insert itself.
- Track a per-instance lastArtifactRowId cursor seeded from the max artifacts rowid at watch() startup
- In checkForChanges(), pick up artifact rows with rowid > cursor written by other instances and re-emit artifact:registered, advancing the cursor
- Advance the cursor on local inserts (insertArtifactRow) so this instance never double-emits its own writes
- Call db.bumpLastModified() in registerArtifact() so other instances' pollers actually look at the artifacts table
- Add regression tests covering cross-instance artifact registration in core store and dashboard artifacts route integration
- Add changeset and doc note for the cross-instance live-refresh fix
Files changed:
.changeset/fn-7544-artifact-cross-instance-live-refresh.md | 7 ++
docs/storage.md | 1 +
packages/core/src/__tests__/artifacts.test.ts | 85 +++++++++++++++
packages/core/src/store.ts | 63 ++++++++++-
packages/dashboard/src/routes/__tests__/artifacts-route-integration.test.ts | 120 ++++++++++++++++++++-
5 files changed, 274 insertions(+), 2 deletions(-)
Fusion-Task-Id: FN-7544
Fusion-Task-Lineage: 76570505-082e-4f9a-8b06-29591c06c435
Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
Wires PlannerOverseerMonitor/PlannerRecoveryController decision points (human-control withholds, confirmation requests/resolutions, and related overseer stages) to the FN-7520 emitOverseer* façade using the real TaskStore, so the planner-oversight intervention timeline now populates from real engine activity instead of staying empty.
- Add onConfirmationResolved handler to PlannerRecoveryController, invoked (best-effort, audit-only) from resolveConfirmation for both approved and denied outcomes.
- Wire project-engine.ts to call emitOverseerObservation/emitOverseerEscalation/emitOverseerConfirmation at the real engine decision points, deduped per (task, stage[, signal]).
- Add planner-overseer-intervention-wiring.test.ts covering the new wiring end-to-end.
- Update docs/architecture.md to reflect the wiring.
- Add changeset fn-7551-overseer-timeline-wiring.md (patch).
Files changed:
.changeset/fn-7551-overseer-timeline-wiring.md | 7 +
docs/architecture.md | 2 +-
.../planner-overseer-intervention-wiring.test.ts | 319 +++++++++++++++++++++
packages/engine/src/planner-recovery-controller.ts | 36 +++
packages/engine/src/project-engine.ts | 248 +++++++++++++++-
5 files changed, 607 insertions(+), 5 deletions(-)
Fusion-Task-Id: FN-7551
Fusion-Task-Lineage: 8bcd103e-8797-4ef5-9b68-bd2daec8d26b
Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
Adds a mobile-friendly overflow menu for the task-detail planner-overseer controls, keeping the desktop inline cluster unchanged.
- Below 768px, replace the inline oversight-level select/nudge/stop/explain controls with a single "Oversight" overflow-menu trigger (MoreVertical icon, aria-haspopup="menu") mirroring the existing move-action dropdown pattern
- Popover lists the same controls as full-width, tappable role="menuitem" entries with identical testids and enablement/visibility rules as desktop
- Trigger is withheld entirely when no controls would render (no empty-shell button)
- Desktop (>768px) inline cluster is unchanged
- Adds TaskDetailModal.oversight-mobile.test.tsx covering trigger visibility, menu contents, and parity with desktop controls
- Updates docs/dashboard-guide.md to document the mobile overflow-menu behavior
Files changed:
docs/dashboard-guide.md | 2 +
packages/dashboard/app/components/TaskDetailModal.css | 118 ++++++
packages/dashboard/app/components/TaskDetailModal.tsx | 383 ++++++++++++++-----
packages/dashboard/app/components/__tests__/TaskDetailModal.oversight-mobile.test.tsx | 413 +++++++++++++++++++++
packages/dashboard/app/components/__tests__/TaskDetailModal.test-helpers.ts | 2 +
5 files changed, 830 insertions(+), 88 deletions(-)
Fusion-Task-Id: FN-7545
Fusion-Task-Lineage: 63cb778e-f17d-43d3-a3de-864683d4e8f1
Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
Removes the recently-added executor/reviewer/merger overseer-state chip from TaskCard, which fired as noise on nearly every in-progress card, keeping the separate oversight-level badge untouched.
- Deletes deriveOverseerCardWatchedStage and its OVERSEER_STATE_LABEL/MODIFIER maps from TaskCard.tsx
- Removes showOverseerStateBadge gating and its contribution to hasCardMetaBadges/areTaskCardPropsEqual
- Strips the now-unused .task-card-overseer-state-badge CSS rules from TaskCard.css
- Rewrites TaskCard.oversight.test.tsx to drop overseer-state-badge coverage while keeping oversight-level badge tests
- Updates docs/dashboard-guide.md reference and adds a changeset documenting the removal
Files changed:
.changeset/fn-7542-remove-overseer-state-badge.md | 7 +
docs/dashboard-guide.md | 2 +-
packages/dashboard/app/components/TaskCard.css | 63 +-------
packages/dashboard/app/components/TaskCard.tsx | 161 +++------------------
.../__tests__/TaskCard.oversight.test.tsx | 149 +++++++------------
5 files changed, 77 insertions(+), 305 deletions(-)
Fusion-Task-Id: FN-7542
Fusion-Task-Lineage: dabaf72a-a7b0-4054-8a72-7c3d99fa7bb1
Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
Adds a canonical emission layer over recordPlannerIntervention so overseer decision points (observation, steering, recovery attempt, retry, confirmation, escalation) emit consistent overseer:intervention run-audit events without inlining action/outcome logic at each call-site.
- Add packages/core/src/planner-overseer-events.ts with emitOverseerObservation, emitOverseerSteering, emitOverseerRecoveryAttempt, emitOverseerRetry, emitOverseerConfirmation, and emitOverseerEscalation, each fixing its category's intervention action/default outcome and delegating to recordPlannerIntervention.
- Export the new emitters and OverseerEventInput type from packages/core/src/index.ts.
- Add unit tests covering each emitter's action/outcome mapping and metadata pass-through.
- Add a minor changeset documenting the new run-audit emission facade for planner-overseer events.
- Update docs/architecture.md's Run Audit API section to describe the FN-7520 emission facade and its relationship to FN-7519's overseer:intervention mutation type.
Files changed:
.changeset/fn-7520-planner-overseer-events.md | 7 +
docs/architecture.md | 2 +-
packages/core/src/__tests__/planner-overseer-events.test.ts | 236 +++++++++++++++++++++
packages/core/src/index.ts | 9 +
packages/core/src/planner-overseer-events.ts | 128 +++++++++++
5 files changed, 381 insertions(+), 1 deletion(-)
Fusion-Task-Id: FN-7520
Fusion-Task-Lineage: 85e0d761-e4f3-437e-abe2-031e6cf89c1c
Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
Prevent the Auto-recovery/oversight badge from rendering on virtually every task card when the effective planner oversight level is just the inherited schema default.
- TaskCard: track whether the effective oversight level was reached purely via inheritance (no per-task override, no explicit non-default workflow tier), and suppress the badge in that case
- Preserve badge visibility when a task explicitly overrides oversight to "autonomous" or a workflow tier explicitly resolves to a non-default level
- Update TaskCard oversight tests to cover inherited-default suppression vs explicit-override rendering
- Add changeset documenting the fix
- Update dashboard guide docs
Files changed:
.changeset/fn-7539-oversight-badge-default.md | 7 ++++
docs/dashboard-guide.md | 3 +-
packages/dashboard/app/components/TaskCard.tsx | 21 +++++++++-
.../__tests__/TaskCard.oversight.test.tsx | 48 +++++++++++++++++++---
.../app/components/__tests__/TaskCard.test.tsx | 11 +++--
5 files changed, 76 insertions(+), 14 deletions(-)
Fusion-Task-Id: FN-7539
Fusion-Task-Lineage: 8ac9a60c-69c6-4095-b6b2-18c79718b62b
Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
Introduces a persisted planner-overseer intervention timeline surfaced in the task-detail Planner Oversight cluster, recording stage, reason, action taken, outcome, attempt count/limit, and source links for each intervention.
- Add core `PlannerInterventionEntry` type plus `recordPlannerIntervention`/`getPlannerInterventionTimeline` helpers that persist entries via the run-audit store under the `overseer:intervention` mutation
- Add `PlannerInterventionTimeline` dashboard component rendering the timeline (stage/reason/action/outcome/attempts/links) with associated styles
- Wire the new API route/legacy handler and TaskDetailModal integration to expose and render the timeline
- Add unit tests for the core helpers and the new UI component
- Add changeset for the new minor feature and update architecture/dashboard-guide docs
Files changed:
$(cat /tmp/diffstat_7519.txt)
Fusion-Task-Id: FN-7519
Fusion-Task-Lineage: 3c4fcda3-9eb2-46d3-b142-b0c7d6334cd0
Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
Route window resize/orientationchange/scroll through the same opening-viewport guard used by visualViewport so a same-gesture echo repositions the Activity menu instead of closing it, fixing the dropdown not showing on mobile.
- Add handleGuardedViewportChange shared by resize/orientationchange/visualViewport listeners so an opening-gesture echo only repositions the menu instead of closing it
- Add handleScrollChange to treat scrolls originating inside the .detail-tabs horizontal tab strip as benign (reposition-only), since window scroll listens with capture and would otherwise see nested scroller scrolls as a close signal
- Add regression tests covering mobile Activity dropdown open/scroll/resize behavior
- Add changeset for the fix
- Update dashboard guide docs
Files changed:
.changeset/fn-7536-activity-dropdown-mobile.md | 7 +
docs/dashboard-guide.md | 1 +
.../dashboard/app/components/TaskDetailModal.tsx | 38 +++--
.../TaskDetailModal.task-activity-chat.test.tsx | 161 +++++++++++++++++++++
4 files changed, 196 insertions(+), 11 deletions(-)
Fusion-Task-Id: FN-7536
Fusion-Task-Lineage: c8a3b11e-9bd2-4a61-b49e-038c93cfe4d2
Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
Branch-group completion no longer silently drops archived-but-unlanded members, which previously let genuinely-incomplete groups be flagged complete and promoted.
- listTasksByBranchGroup now scans with includeArchived:true so archived members stay counted in the group's total instead of dropping out silently
- ArchivedTaskEntry gains a persisted mergeDetails snapshot so an archived member that had already landed is still distinguished from one that never landed
- store.ts archival paths (task->archive projection) now carry mergeDetails through so isBranchGroupMemberLanded keeps working post-archival
- Added regression coverage in branch-group-store.test.ts and group-merge-coordinator.test.ts for archived-landed and archived-unlanded gating
- Added changeset documenting the fix as a patch-level bug fix
Files changed:
.changeset/fn-7534-branch-group-archived-member.md | 7 +
docs/dashboard-guide.md | 2 +
packages/core/src/__tests__/branch-group-store.test.ts | 77 +++++++++++
packages/core/src/store.ts | 30 ++++-
packages/core/src/types.ts | 11 ++
packages/engine/src/__tests__/group-merge-coordinator.test.ts | 148 ++++++++++++++++++++-
6 files changed, 273 insertions(+), 2 deletions(-)
Fusion-Task-Id: FN-7534
Fusion-Task-Lineage: 510af857-ce08-49a0-a2a1-41b3ad473804
Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
Adds two display-only badges to TaskCard so operators can see planner oversight configuration and live overseer activity at a glance.
- Resolve and display the workflow's effective `plannerOversightLevel` (observe/steer/autonomous) per task, with a module-level cache keyed by (projectId, workflowId) and in-flight de-duplication to avoid redundant `/api/workflows/:id/setting-values` fetches across cards sharing a workflow.
- Gate oversight badge rendering until the workflow-effective value resolves (or a per-task override is known) so the very first render never shows a guessed schema-default badge.
- Derive a card-local "active overseer state" indicator (Executor/Reviewer/Merger/Pull request/Workflow gate) that mirrors the engine's `resolveWatchedStage` precedence using only fields already present on the Task payload (column, paused, pausedReason, prInfo, reviewState, workflowTransitionNotification), since the real in-memory monitor state has no persisted/API surface today.
- Add corresponding CSS badge modifiers and update dashboard-guide.md documentation.
- Add TaskCard.oversight.test.tsx covering the new badges and update existing TaskCard tests/badge-wrap test for the new markup.
Files changed:
docs/dashboard-guide.md | 4 +
packages/dashboard/app/components/TaskCard.css | 106 +++++-
packages/dashboard/app/components/TaskCard.tsx | 369 +++++++++++++++++++-
.../__tests__/TaskCard.badge-wrap.test.tsx | 1 +
.../__tests__/TaskCard.oversight.test.tsx | 383 +++++++++++++++++++++
.../app/components/__tests__/TaskCard.test.tsx | 32 +-
6 files changed, 889 insertions(+), 6 deletions(-)
Fusion-Task-Id: FN-7516
Fusion-Task-Lineage: ce909409-862b-4697-a19e-d6735a3b572d
Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>