Commit Graph

874 Commits

Author SHA1 Message Date
Fusion
a039e2cf27 feat(FN-3018): merge fusion/fn-3018
- **feat(engine): sanitize cwd from createFnAgent startup log** — prevents worktree paths from leaking into engine diagnostic logs
- **test(engine): add regression test for cwd redaction** — verifies working directory is not present in logged output
- **test(engine): update routes-agent-skills test** — minimal addition to coverage
- **test(dashboard): stabilize TaskCard test** — fixes flakiness in full-suite gate

Commits merged:
- test(FN-3018): stabilize dashboard tests for full-suite gate
- test(FN-3018): complete Step 2 — add cwd redaction regression
- feat(FN-3018): complete Step 1 — sanitize createFnAgent startup log

Files changed:
.../app/components/__tests__/TaskCard.test.tsx     |  2 +-
 .../src/__tests__/routes-agent-skills.test.ts      |  1 +
 .../src/__tests__/pi-create-fn-agent.test.ts       | 28 ++++++++++++++++++++++
 packages/engine/src/pi.ts                          |  2 +-
 4 files changed, 31 insertions(+), 2 deletions(-)

Fusion-Task-Id: FN-3018
2026-04-29 22:09:05 -07:00
Fusion
7211363845 feat(FN-3003): merge fusion/fn-3003
- Unify notification service ownership across the engine: `Notifier` now operates on `ProjectEngine` rather than its own `Core` reference, resolving stale-closure issues during engine lifecycle
- Add regression tests in `notifier.test.ts` covering merge dedupe wiring and ownership initialization
- Expand `project-engine.test.ts` to cover ownership state transitions and `ProjectEngine`-notifier integration
- Fix TypeScript regressions in `ModelOnboardingModal.tsx` and `SettingsModal.tsx` introduced by custom provider types
- Document the notification ownership model in `docs/architecture.md`

Commits merged:
- feat(FN-3003): complete Step 4 — document notification ownership model
- fix(FN-3003): resolve dashboard custom provider typecheck regressions
- test(FN-3003): complete Step 2 — add merge dedupe wiring regressions
- feat(FN-3003): complete Step 1 — unify notification service ownership

Files changed:
docs/architecture.md                               |  2 +
 .../app/components/ModelOnboardingModal.tsx        | 13 +++++-
 .../dashboard/app/components/SettingsModal.tsx     | 17 +++++--
 packages/engine/src/__tests__/notifier.test.ts     | 33 ++++++++++++++
 .../engine/src/__tests__/project-engine.test.ts    | 53 +++++++++++++++++++---
 packages/engine/src/notifier.ts                    |  3 +-
 packages/engine/src/project-engine.ts              | 12 +++--
 7 files changed, 116 insertions(+), 17 deletions(-)

Fusion-Task-Id: FN-3003
2026-04-29 21:48:13 -07:00
Fusion
38f5159719 feat(FN-2962): merge fusion/fn-2962
- Add changeset for `@runfusion/fusion` minor release introducing custom provider registration support

Commits merged:
- feat(FN-2962): complete Step 8 — add changeset and documentation

Files changed:
.changeset/register-custom-providers.md | 5 +++++
 1 file changed, 5 insertions(+)

Fusion-Task-Id: FN-2962
2026-04-29 21:42:44 -07:00
Fusion
101567052c feat(FN-2989): merge fusion/fn-2989
- Add `CustomProviderForm` component and routing for user-defined OpenAI/Anthropic-compatible endpoints (FN-2987)
- Expand `agent-heartbeat.ts` with richer prompt guidance and criteria updates
- Strengthen executor prompts in `executor.ts` with expanded implementation guidance
- Enrich reviewer prompts in `reviewer.ts` with enhanced review criteria
- Add triage prompt guidance in `triage.ts` for task classification
- Extend merger prompts in `merger.ts` with merge strategy guidance
- Add `agent-document-tools.test.ts` and `agent-tools.test.ts` integration tests
- Expand `store.ts` with new task/document operations; add `task-documents.test.ts` and `store.test.ts`
- Add legacy API route in `dashboard/app/api/legacy.ts` and auth path routing
- Update `ModelOnboardingModal` with custom provider onboarding flow
- Extend `SettingsModal` with custom provider configuration UI and mobile layout tests
- Add changeset for `@runfusion/fusion` minor release

Commits merged:
- feat(FN-2989): complete Step 5 — enrich remaining agent prompts
- feat(FN-2989): complete Step 4 — enrich reviewer prompt criteria
- feat(FN-2989): complete Step 3 — strengthen triage prompt guidance
- feat(FN-2989): complete Step 2 — expand executor guidance prompts
- feat(FN-2989): complete Step 1 — expand heartbeat prompts
- feat(FN-2987): merge fusion/fn-2987
- feat(FN-2959): merge fusion/fn-2959-2

Files changed:
.changeset/custom-openai-anthropic-providers.md    |   5 +
 docs/task-management.md                            |   4 +
 packages/core/src/__tests__/store.test.ts          |  38 +++
 packages/core/src/__tests__/task-documents.test.ts |  32 +++
 packages/core/src/store.ts                         |  34 ++-
 packages/dashboard/app/api/legacy.ts               |  41 ++++
 .../app/components/CustomProviderForm.css          |  45 ++++
 .../app/components/CustomProviderForm.tsx          | 203 ++++++++++++++++
 .../app/components/ModelOnboardingModal.css        |  14 ++
 .../app/components/ModelOnboardingModal.tsx        |  62 ++++-
 .../dashboard/app/components/SettingsModal.css     |  38 +++
 .../dashboard/app/components/SettingsModal.tsx     |  91 +++++++-
 .../__tests__/CustomProviderForm.test.tsx          |  60 +++++
 .../__tests__/ModelOnboardingModal.test.tsx        |  23 ++
 .../components/__tests__/SettingsModal.test.tsx    |  13 ++
 .../__tests__/SettingsModalNodeRouting.test.tsx    |   4 +
 .../components/__tests__/settings-mobile.test.tsx  |   4 +
 packages/dashboard/src/auth-paths.ts               |   4 +
 packages/dashboard/src/routes.ts                   |   2 +
 .../__tests__/custom-provider-routes.test.ts       | 118 ++++++++++
 .../src/routes/register-custom-provider-routes.ts  | 254 +++++++++++++++++++++
 .../src/__tests__/agent-document-tools.test.ts     |  15 ++
 packages/engine/src/__tests__/agent-tools.test.ts  |  43 ++++
 packages/engine/src/agent-heartbeat.ts             | 107 +++++++--
 packages/engine/src/agent-reflection.ts            |   7 +-
 packages/engine/src/agent-tools.ts                 |  28 ++-
 packages/engine/src/cron-runner.ts                 |   4 +
 packages/engine/src/executor.ts                    |  69 +++++-
 packages/engine/src/merger.ts                      |  26 ++-
 packages/engine/src/mission-execution-loop.ts      |   6 +
 packages/engine/src/reviewer.ts                    |  24 ++
 packages/engine/src/step-session-executor.ts       |  11 +-
 packages/engine/src/triage.ts                      |  20 ++
 33 files changed, 1407 insertions(+), 42 deletions(-)

Fusion-Task-Id: FN-2989
2026-04-29 20:27:04 -07:00
Fusion
9d3a4c7ca5 feat(FN-2987): merge fusion/fn-2987
- **Archived task read-only enforcement** — `packages/core/src/store.ts` adds guards that prevent log entries and documents from being written to archived tasks; `packages/engine/src/agent-tools.ts` surfaces meaningful errors instead of silent failures when tools are called on archived tasks
- **Type safety for archived guards** — resolved typecheck issues around the archived-task write guards in core
- **Tests for archived behavior** — added regression coverage in `packages/core/src/__tests__/store.test.ts` and `packages/core/src/__tests__/task-documents.test.ts`; also added `packages/engine/src/__tests__/agent-tools.test.ts` to cover archived-task error paths
- **Documentation** — updated `docs/task-management.md` to document the archived read-only behavior
- **Also merged** `fusion/fn-2959-2` (CustomProviderForm component, SettingsModal routing, custom provider API routes, and associated tests)

Commits merged:
- feat(FN-2987): complete Step 6 — document archived read-only behavior
- fix(FN-2987): complete Step 5 — resolve typecheck for archived guards
- test(FN-2987): complete Step 4 — add archived-task regression coverage
- feat(FN-2987): complete Step 3 — handle archived log tool errors
- feat(FN-2987): complete Step 2 — enforce archived document write guard
- feat(FN-2987): complete Step 1 — harden archived logEntry checks
- feat(FN-2959): merge fusion/fn-2959-2

Files changed:
.changeset/custom-openai-anthropic-providers.md    |   5 +
 docs/task-management.md                            |   4 +
 packages/core/src/__tests__/store.test.ts          |  38 +++
 packages/core/src/__tests__/task-documents.test.ts |  32 +++
 packages/core/src/store.ts                         |  34 ++-
 packages/dashboard/app/api/legacy.ts               |  41 ++++
 .../app/components/CustomProviderForm.css          |  45 ++++
 .../app/components/CustomProviderForm.tsx          | 203 ++++++++++++++++
 .../app/components/ModelOnboardingModal.css        |  14 ++
 .../app/components/ModelOnboardingModal.tsx        |  62 ++++-
 .../dashboard/app/components/SettingsModal.css     |  38 +++
 .../dashboard/app/components/SettingsModal.tsx     |  91 +++++++-
 .../__tests__/CustomProviderForm.test.tsx          |  60 +++++
 .../__tests__/ModelOnboardingModal.test.tsx        |  23 ++
 .../components/__tests__/SettingsModal.test.tsx    |  13 ++
 .../__tests__/SettingsModalNodeRouting.test.tsx    |   4 +
 .../components/__tests__/settings-mobile.test.tsx  |   4 +
 packages/dashboard/src/auth-paths.ts               |   4 +
 packages/dashboard/src/routes.ts                   |   2 +
 .../__tests__/custom-provider-routes.test.ts       | 118 ++++++++++
 .../src/routes/register-custom-provider-routes.ts  | 254 +++++++++++++++++++++
 .../src/__tests__/agent-document-tools.test.ts     |  15 ++
 packages/engine/src/__tests__/agent-tools.test.ts  |  43 ++++
 packages/engine/src/agent-tools.ts                 |  28 ++-
 24 files changed, 1166 insertions(+), 9 deletions(-)

Fusion-Task-Id: FN-2987
2026-04-29 20:18:46 -07:00
Fusion
5c82c3f34a feat(FN-2947): merge fusion/fn-2947
- Add AI-powered merge commit summarization with toggle in Settings (`packages/core/src/ai-summarize.ts`, `packages/dashboard/app/components/SettingsModal.tsx`, `packages/engine/src/merger.ts`)
- Introduce `generateMergeCommitSummary` utility in `@fusion/core` that calls the configured model with a diff prompt; results appear in the commit log for merge commits
- Add `aiMergeSummaryEnabled` project setting and `useAiMergeSummaryEnabled` hook in dashboard
- Update `@runfusion/fusion` CLI extension to surface the new setting
- Add `POST /api/tasks/:id/workflow/pre-merge` and `POST /api/tasks/:id/planning-subtask` routes (`packages/dashboard/src/routes/`)
- Rework InsightsView with two-pane layout for better readability (`packages/dashboard/app/components/InsightsView.tsx`, `packages/dashboard/app/components/InsightsView.css`)
- Fix `task plan` command to surface errors when planning fails (`packages/cli/src/commands/task.ts`)
- Add tests for `ai-summarize`, `agent-heartbeat`, `agent-tools`, and dashboard API routes; refresh onboarding and agents-view mobile tests
- Minor CSS polish: reduce AgentsView panel hover gap, improve CommitDiffTab layout, add ScriptsModal styles
- Changelog and version bumps for `@runfusion/fusion` v0.9.1, CLI alias, and all workspace packages

Commits merged:
- feat(FN-2947): complete Step 7 — add changeset and docs deliverables
- fix(FN-2947): clean unused aiSummary path
- test(FN-2947): complete Step 5 — cover AI merge summary flow
- feat(FN-2947): complete Step 4 — add merge summary settings toggle
- feat(FN-2947): complete Step 3 — wire AI merge summaries into merger
- feat(FN-2947): complete Step 2 — add merge summary setting
- feat(FN-2947): complete Step 1 — add merge commit summarizer
- feat(FN-2970): merge fusion/fn-2970
- feat(FN-2956): merge fusion/fn-2956
- feat(FN-2923): merge fusion/fn-2923
- feat(FN-2945): merge fusion/fn-2945
- chore(release): v0.9.1
- fix(FN-XXX): improve git manager diff layout
- fix(dashboard): rework Insights view with two-pane layout
- fix(FN-XXX): keep experimental views off by default

Files changed:
.changeset/active-agents-no-stuck-connecting.md    |  13 --
 .changeset/active-agents-panel-hoist-heartbeat.md  |  13 --
 .changeset/add-ai-merge-commit-summary.md          |   5 +
 .changeset/fix-agent-heartbeat-terminal-links.md   |   5 -
 .changeset/show-planning-tasks-immediately.md      |   5 -
 CHANGELOG.md                                       |  92 ++++++++++
 docs/settings-reference.md                         |   3 +
 package.json                                       |   2 +-
 packages/cli-alias/CHANGELOG.md                    |  15 ++
 packages/cli-alias/package.json                    |   2 +-
 packages/cli/CHANGELOG.md                          |  12 ++
 packages/cli/package.json                          |   2 +-
 packages/cli/src/__tests__/task-plan.test.ts       |   1 +
 packages/cli/src/commands/__tests__/task.test.ts   |  15 +-
 packages/cli/src/commands/task.ts                  |  15 +-
 packages/cli/src/extension.ts                      |   9 +
 packages/core/CHANGELOG.md                         |   7 +
 packages/core/package.json                         |   2 +-
 packages/core/src/__tests__/ai-summarize.test.ts   |  64 +++++++
 packages/core/src/ai-summarize.ts                  | 114 ++++++++++++
 packages/core/src/index.ts                         |   3 +
 packages/core/src/settings-schema.ts               |   1 +
 packages/core/src/types.ts                         |   4 +
 packages/dashboard/CHANGELOG.md                    |  14 ++
 packages/dashboard/app/App.tsx                     |  28 ++-
 .../app/__tests__/agent-css-classes.test.ts        |   3 +-
 packages/dashboard/app/__tests__/api.test.ts       |  13 ++
 packages/dashboard/app/api/legacy.ts               |   2 +
 packages/dashboard/app/components/AgentsView.css   |  22 +--
 .../dashboard/app/components/CommitDiffTab.tsx     |   2 +-
 .../dashboard/app/components/GitManagerModal.tsx   |  71 ++++----
 packages/dashboard/app/components/InsightsView.css | 200 ++++++++++++++++++---
 packages/dashboard/app/components/InsightsView.tsx | 111 ++++++++----
 .../app/components/ModelOnboardingModal.tsx        |   5 +-
 packages/dashboard/app/components/ScriptsModal.css |  89 +++++++++
 .../dashboard/app/components/SettingsModal.css     |  11 +-
 .../dashboard/app/components/SettingsModal.tsx     |  22 ++-
 .../dashboard/app/components/TaskDetailModal.css   |   5 +
 packages/dashboard/app/components/TodoView.tsx     |   2 +
 .../app/components/__tests__/App.test.tsx          |  33 ++++
 .../app/components/__tests__/InsightsView.test.tsx |  25 ++-
 .../__tests__/ModelOnboardingModal.test.tsx        |   4 +-
 .../app/components/__tests__/QuickChatFAB.test.tsx |  14 +-
 .../__tests__/SettingsModalNodeRouting.test.tsx    |  12 +-
 .../app/components/__tests__/TodoView.test.tsx     |   4 +-
 .../__tests__/agents-view-mobile.test.tsx          |   5 +-
 .../components/__tests__/onboarding-flow.test.tsx  |   2 +-
 .../app/hooks/__tests__/useAppSettings.test.ts     |   1 +
 .../app/hooks/__tests__/useTaskHandlers.test.ts    |   4 +-
 packages/dashboard/app/hooks/useAppSettings.ts     |  12 ++
 packages/dashboard/app/hooks/useTaskHandlers.ts    |   4 +-
 packages/dashboard/package.json                    |   2 +-
 packages/dashboard/src/__tests__/routes.test.ts    |  14 ++
 packages/dashboard/src/routes.ts                   |   4 +
 .../dashboard/src/routes/register-git-github.ts    |  12 ++
 .../src/routes/register-planning-subtask-routes.ts |   3 +
 .../src/routes/register-task-workflow-routes.ts    |   7 +
 packages/desktop/CHANGELOG.md                      |   7 +
 packages/desktop/package.json                      |   2 +-
 packages/engine/CHANGELOG.md                       |  11 ++
 packages/engine/package.json                       |   2 +-
 .../engine/src/__tests__/agent-heartbeat.test.ts   |  10 ++
 .../src/__tests__/agent-tools-delegation.test.ts   |   2 +
 packages/engine/src/__tests__/agent-tools.test.ts  |  37 ++++
 packages/engine/src/__tests__/cron-runner.test.ts  |   4 +
 packages/engine/src/__tests__/merger.test.ts       |  75 ++++++++
 .../src/__tests__/node-routing-policy.test.ts      |  25 ++-
 .../src/__tests__/pr-comment-handler.test.ts       |   8 +
 .../src/__tests__/scheduler-node-routing.test.ts   |  18 +-
 packages/engine/src/__tests__/triage.test.ts       |   2 +
 packages/engine/src/agent-heartbeat.ts             |  13 +-
 packages/engine/src/agent-tools.ts                 |  13 +-
 packages/engine/src/cron-runner.ts                 |   7 +-
 packages/engine/src/executor.ts                    |   2 +-
 packages/engine/src/merger.ts                      | 181 ++++++++-----------
 packages/engine/src/mission-execution-loop.ts      |   8 +
 packages/engine/src/pr-comment-handler.ts          |   5 +
 packages/engine/src/project-engine.ts              |   8 +
 packages/engine/src/routine-runner.ts              |   4 +
 packages/engine/src/scheduler.ts                   |   8 +-
 packages/engine/src/triage.ts                      |   4 +
 packages/mobile/CHANGELOG.md                       |   7 +
 packages/mobile/package.json                       |   2 +-
 packages/pi-claude-cli/CHANGELOG.md                |   7 +
 packages/pi-claude-cli/package.json                |   2 +-
 packages/plugin-sdk/CHANGELOG.md                   |  10 ++
 packages/plugin-sdk/package.json                   |   2 +-
 .../examples/fusion-plugin-auto-label/CHANGELOG.md |   8 +
 .../examples/fusion-plugin-auto-label/package.json |   2 +-
 .../examples/fusion-plugin-ci-status/CHANGELOG.md  |   8 +
 .../examples/fusion-plugin-ci-status/package.json  |   2 +-
 .../fusion-plugin-notification/CHANGELOG.md        |   8 +
 .../fusion-plugin-notification/package.json        |   2 +-
 .../fusion-plugin-settings-demo/CHANGELOG.md       |   8 +
 .../fusion-plugin-settings-demo/package.json       |   2 +-
 plugins/fusion-plugin-hermes-runtime/CHANGELOG.md  |   8 +
 plugins/fusion-plugin-hermes-runtime/package.json  |   2 +-
 .../fusion-plugin-openclaw-runtime/CHANGELOG.md    |   8 +
 .../fusion-plugin-openclaw-runtime/package.json    |   2 +-
 .../fusion-plugin-paperclip-runtime/CHANGELOG.md   |   8 +
 .../fusion-plugin-paperclip-runtime/package.json   |   2 +-
 101 files changed, 1388 insertions(+), 334 deletions(-)

Fusion-Task-Id: FN-2947
2026-04-29 17:38:25 -07:00
Fusion
8a92042fc5 feat(FN-2956): merge fusion/fn-2956
- **FN-2956: Node routing policy guard** — `scheduler.ts` and `agent-heartbeat.ts` now enforce unavailable-node dispatch prevention; adds policy-matrix unit tests (`node-routing-policy.test.ts`) and integration tests (`scheduler-node-routing.test.ts`)
- **Agent delegation tools** — `agent-tools.ts` gains delegation functionality; `agent-heartbeat.ts` updated with monitoring hooks; 37 new lines of test coverage in `agent-tools.test.ts`
- **Dashboard route registration** — New routes added for git/github registration (`register-git-github.ts`), planning subtasks, and task workflows; test coverage in `routes.test.ts`
- **CLI task planning** — `task.ts` and `extension.ts` updated to support task planning flow; task-plan test extended
- **Engine integrations** — `cron-runner.ts`, `pr-comment-handler.ts`, `triage.ts`, `mission-execution-loop.ts`, `project-engine.ts`, and `routine-runner.ts` all updated with expanded tests and feature hooks
- **Dashboard UI/hooks** — `useTaskHandlers.ts` and related tests updated; `ModelOnboardingModal.tsx` and `TodoView.tsx` refined; CSS adjustments in `AgentsView.css`
- Includes merged work from `fusion/fn-2923` and `fusion/fn-2945`

Commits merged:
- test(FN-2956): complete Step 4 — align scheduler routing policy integration tests
- test(FN-2956): complete Step 3 — expand policy matrix unit coverage
- feat(FN-2956): complete Step 2 — enforce unavailable-node policy dispatch guard
- feat(FN-2923): merge fusion/fn-2923
- feat(FN-2945): merge fusion/fn-2945

Files changed:
packages/cli/src/__tests__/task-plan.test.ts       |  1 +
 packages/cli/src/commands/__tests__/task.test.ts   | 15 +++++++--
 packages/cli/src/commands/task.ts                  | 15 ++++++++-
 packages/cli/src/extension.ts                      |  9 ++++++
 .../app/__tests__/agent-css-classes.test.ts        |  3 +-
 packages/dashboard/app/__tests__/api.test.ts       | 13 ++++++++
 packages/dashboard/app/api/legacy.ts               |  2 ++
 packages/dashboard/app/components/AgentsView.css   | 22 ++++++-------
 .../app/components/ModelOnboardingModal.tsx        |  5 ++-
 packages/dashboard/app/components/TodoView.tsx     |  2 ++
 .../__tests__/ModelOnboardingModal.test.tsx        |  4 +--
 .../app/components/__tests__/QuickChatFAB.test.tsx | 14 ++------
 .../app/components/__tests__/TodoView.test.tsx     |  4 +--
 .../__tests__/agents-view-mobile.test.tsx          |  5 ++-
 .../components/__tests__/onboarding-flow.test.tsx  |  2 +-
 .../app/hooks/__tests__/useTaskHandlers.test.ts    |  4 +--
 packages/dashboard/app/hooks/useTaskHandlers.ts    |  4 +--
 packages/dashboard/src/__tests__/routes.test.ts    | 14 ++++++++
 packages/dashboard/src/routes.ts                   |  4 +++
 .../dashboard/src/routes/register-git-github.ts    | 12 +++++++
 .../src/routes/register-planning-subtask-routes.ts |  3 ++
 .../src/routes/register-task-workflow-routes.ts    |  7 ++++
 .../engine/src/__tests__/agent-heartbeat.test.ts   | 10 ++++++
 .../src/__tests__/agent-tools-delegation.test.ts   |  2 ++
 packages/engine/src/__tests__/agent-tools.test.ts  | 37 ++++++++++++++++++++++
 packages/engine/src/__tests__/cron-runner.test.ts  |  4 +++
 .../src/__tests__/node-routing-policy.test.ts      | 25 +++++++++++++--
 .../src/__tests__/pr-comment-handler.test.ts       |  8 +++++
 .../src/__tests__/scheduler-node-routing.test.ts   | 18 +++++------
 packages/engine/src/__tests__/triage.test.ts       |  2 ++
 packages/engine/src/agent-heartbeat.ts             | 13 ++++++--
 packages/engine/src/agent-tools.ts                 | 13 ++++++--
 packages/engine/src/cron-runner.ts                 |  7 +++-
 packages/engine/src/executor.ts                    |  2 +-
 packages/engine/src/mission-execution-loop.ts      |  8 +++++
 packages/engine/src/pr-comment-handler.ts          |  5 +++
 packages/engine/src/project-engine.ts              |  8 +++++
 packages/engine/src/routine-runner.ts              |  4 +++
 packages/engine/src/scheduler.ts                   |  8 +++--
 packages/engine/src/triage.ts                      |  4 +++
 40 files changed, 280 insertions(+), 62 deletions(-)

Fusion-Task-Id: FN-2956
2026-04-29 17:30:39 -07:00
Fusion
b0cda2398b feat(FN-2923): merge fusion/fn-2923
- Add task `source` provenance field tracking how tasks enter the system (e.g. `"manual"`, `"heartbeat"`, `"executor"`, `"triage"`, `"cron"`, `"routine"`)
- Wire `source` into shared create-task tools, executor agent, triage agent, cron runner, and routine runner
- Add `source` to heartbeat task creation in `agent-heartbeat.ts` and mission execution loop in `mission-execution-loop.ts`
- Wire `source` field into dashboard routes (`register-planning-subtask-routes.ts`, `register-task-workflow-routes.ts`) and GitHub integration (`register-git-github.ts`)
- Add `source` to the API layer and task store handlers in the dashboard
- Expose `source` in the `fn` CLI task command and `task-plan` extension tool
- Add integration tests for provenance across engine (agent-tools, agent-heartbeat, cron-runner, triage, pr-comment-handler) and dashboard routes
- Update existing tests to assert the correct source values
- Bulk-revert stale changeset files that were superseded by the v0.9.1 release commit

Commits merged:
- test(FN-2923): complete Step 14 — align tests with task source provenance
- test(FN-2923): complete Steps 12-13 — update and add provenance tests
- feat(FN-2923): complete Step 10 — wire CLI task provenance
- feat(FN-2923): complete Step 9 — wire dashboard UI and API source fields
- feat(FN-2923): complete Step 8 — wire dashboard route provenance
- feat(FN-2923): complete Step 7 — wire remaining engine provenance
- feat(FN-2923): complete Step 6 — add routine create-task provenance
- feat(FN-2923): complete Step 5 — add cron create-task provenance
- feat(FN-2923): complete Step 4 — add triage subtask source
- feat(FN-2923): complete Step 3 — add executor create-task source
- feat(FN-2923): complete Step 2 — add heartbeat task provenance
- feat(FN-2923): complete Step 1 — add provenance to shared create-task tools
- feat(FN-2945): merge fusion/fn-2945
- chore(release): v0.9.1

Files changed:
.changeset/active-agents-no-stuck-connecting.md    | 13 ---
 .changeset/active-agents-panel-hoist-heartbeat.md  | 13 ---
 .changeset/fix-agent-heartbeat-terminal-links.md   |  5 --
 .changeset/fix-experimental-defaults.md            |  5 --
 .changeset/improve-git-manager-diff-pane.md        |  5 --
 .changeset/insights-two-pane-layout.md             |  5 --
 .changeset/show-planning-tasks-immediately.md      |  5 --
 CHANGELOG.md                                       | 92 ++++++++++++++++++++++
 package.json                                       |  2 +-
 packages/cli-alias/CHANGELOG.md                    | 15 ++++
 packages/cli-alias/package.json                    |  2 +-
 packages/cli/CHANGELOG.md                          | 12 +++
 packages/cli/package.json                          |  2 +-
 packages/cli/src/__tests__/task-plan.test.ts       |  1 +
 packages/cli/src/commands/__tests__/task.test.ts   | 15 +++-
 packages/cli/src/commands/task.ts                  | 15 +++-
 packages/cli/src/extension.ts                      |  9 +++
 packages/core/CHANGELOG.md                         |  7 ++
 packages/core/package.json                         |  2 +-
 packages/dashboard/CHANGELOG.md                    | 14 ++++
 .../app/__tests__/agent-css-classes.test.ts        |  3 +-
 packages/dashboard/app/__tests__/api.test.ts       | 13 +++
 packages/dashboard/app/api/legacy.ts               |  2 +
 packages/dashboard/app/components/AgentsView.css   | 22 +++---
 .../app/components/ModelOnboardingModal.tsx        |  5 +-
 packages/dashboard/app/components/TodoView.tsx     |  2 +
 .../__tests__/ModelOnboardingModal.test.tsx        |  4 +-
 .../app/components/__tests__/QuickChatFAB.test.tsx | 14 +---
 .../app/components/__tests__/TodoView.test.tsx     |  4 +-
 .../__tests__/agents-view-mobile.test.tsx          |  5 +-
 .../components/__tests__/onboarding-flow.test.tsx  |  2 +-
 .../app/hooks/__tests__/useTaskHandlers.test.ts    |  4 +-
 packages/dashboard/app/hooks/useTaskHandlers.ts    |  4 +-
 packages/dashboard/package.json                    |  2 +-
 packages/dashboard/src/__tests__/routes.test.ts    | 14 ++++
 packages/dashboard/src/routes.ts                   |  4 +
 .../dashboard/src/routes/register-git-github.ts    | 12 +++
 .../src/routes/register-planning-subtask-routes.ts |  3 +
 .../src/routes/register-task-workflow-routes.ts    |  7 ++
 packages/desktop/CHANGELOG.md                      |  7 ++
 packages/desktop/package.json                      |  2 +-
 packages/engine/CHANGELOG.md                       | 11 +++
 packages/engine/package.json                       |  2 +-
 .../engine/src/__tests__/agent-heartbeat.test.ts   | 10 +++
 .../src/__tests__/agent-tools-delegation.test.ts   |  2 +
 packages/engine/src/__tests__/agent-tools.test.ts  | 37 +++++++++
 packages/engine/src/__tests__/cron-runner.test.ts  |  4 +
 .../src/__tests__/pr-comment-handler.test.ts       |  8 ++
 packages/engine/src/__tests__/triage.test.ts       |  2 +
 packages/engine/src/agent-heartbeat.ts             | 13 ++-
 packages/engine/src/agent-tools.ts                 | 13 ++-
 packages/engine/src/cron-runner.ts                 |  7 +-
 packages/engine/src/executor.ts                    |  2 +-
 packages/engine/src/mission-execution-loop.ts      |  8 ++
 packages/engine/src/pr-comment-handler.ts          |  5 ++
 packages/engine/src/project-engine.ts              |  8 ++
 packages/engine/src/routine-runner.ts              |  4 +
 packages/engine/src/triage.ts                      |  4 +
 packages/mobile/CHANGELOG.md                       |  7 ++
 packages/mobile/package.json                       |  2 +-
 packages/pi-claude-cli/CHANGELOG.md                |  7 ++
 packages/pi-claude-cli/package.json                |  2 +-
 packages/plugin-sdk/CHANGELOG.md                   | 10 +++
 packages/plugin-sdk/package.json                   |  2 +-
 .../examples/fusion-plugin-auto-label/CHANGELOG.md |  8 ++
 .../examples/fusion-plugin-auto-label/package.json |  2 +-
 .../examples/fusion-plugin-ci-status/CHANGELOG.md  |  8 ++
 .../examples/fusion-plugin-ci-status/package.json  |  2 +-
 .../fusion-plugin-notification/CHANGELOG.md        |  8 ++
 .../fusion-plugin-notification/package.json        |  2 +-
 .../fusion-plugin-settings-demo/CHANGELOG.md       |  8 ++
 .../fusion-plugin-settings-demo/package.json       |  2 +-
 plugins/fusion-plugin-hermes-runtime/CHANGELOG.md  |  8 ++
 plugins/fusion-plugin-hermes-runtime/package.json  |  2 +-
 .../fusion-plugin-openclaw-runtime/CHANGELOG.md    |  8 ++
 .../fusion-plugin-openclaw-runtime/package.json    |  2 +-
 .../fusion-plugin-paperclip-runtime/CHANGELOG.md   |  8 ++
 .../fusion-plugin-paperclip-runtime/package.json   |  2 +-
 78 files changed, 499 insertions(+), 115 deletions(-)

Fusion-Task-Id: FN-2923
2026-04-29 17:23:22 -07:00
gsxdsm
2febbc9d3a fix(merger): build commit messages from actual content, not branch range
The merge commit message was built from `commitLog`/`diffStat` computed
against `merge-base(branch, main)`. Under squash-merge workflows, when an
earlier task is squash-merged onto main first, branches that forked off
the pre-squash main no longer share ancestry with it — `merge-base`
resolves to a point before the earlier task, and the message describes
work already merged via the prior squash. FN-2952's commit body claimed
11 files / 557 insertions when the actual diff was 2 files / 55 lines.

Subject was also a generic `merge <branch>` regardless of content.

- packages/engine/src/merger.ts: new `computeActualMergeCommitContext`
  helper that derives commitLog/diffStat from the actual integration
  delta (`git diff --cached <integrationTarget> --stat`), filtering
  branch commits by patch-id against the target's recent history to
  drop already-squashed siblings. Wired into both commit-finalization
  sites (`commitOrAmendMergeWithFixes` uses `preAttemptHeadSha`; the
  final amend in `runMergeAttempt` uses `HEAD~1`). Agent-context use of
  the wide range is unchanged.
- packages/engine/src/merger.ts: `buildDeterministicMergeMessage` now
  generates subject and body in parallel via `Promise.all`. Subject is
  composed as `feat(taskId): <ai summary>`, capped at 72 chars, with
  fallback to the legacy `merge <branch>` form on any AI failure.
- packages/core/src/ai-summarize.ts: new `summarizeCommitSubject` and
  `sanitizeCommitSubject` mirroring the body summarizer's structure.
  Same title-summarizer lane, 15s timeout. Sanitizer strips quotes,
  bullets, re-added conventional-commit prefixes, and trailing periods;
  hard-caps at 60 chars.
- packages/core/src/__tests__/ai-summarize.test.ts: 9 tests covering
  the sanitizer's behavior.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-29 15:22:20 -07:00
Fusion
8793d4761e feat(FN-2943): merge fusion/fn-2943
- **Active Agents panel overhaul**: Hoisted above the main agent list with a new `ActiveAgentsPanel.css` stylesheet; shows next heartbeat ETA and prevents cards from getting stuck in "Connecting..." state
- **Node routing policy**: Scheduler now enforces unavailable-node scheduling rules and routes tasks to eligible nodes more reliably (`packages/engine/src/scheduler.ts`, `node-routing-policy.ts`)
- **Task card merge timer**: Keeps the card timer live while a task is actively merging rather than resetting on merge start
- **Settings modal scroll**: Scrolls to top when auth actions succeed, with regression tests added
- **Task steps reset**: Resets steps when reopening a task that returns to todo or triage (FN-2966)
- **Core store additions**: New `packages/core/src/store.ts` test coverage for task state mutations
- **Dashboard SSE bus**: New `sse-bus.ts` for event bus communication in the dashboard
- **Version bumps**: `v0.9.0` release across all packages; `cli-alias`, `dashboard`, `engine`, `plugin-sdk`, and plugin changelogs updated
- **Changeset cleanup**: Removed 20 stale changeset files consumed by the v0.9.0 release; added two new ones for Active Agents panel fixes

Commits merged:
- test(FN-2943): complete Step 3 — add auth scroll-to-top regressions
- feat(FN-2943): complete Step 2 — scroll on successful auth actions
- feat(FN-2943): complete Step 1 — add settings content scroll ref
- feat(FN-2951): enforce unavailable-node scheduling and improve active agents panel
- chore: add changeset for Active Agents panel hoist + heartbeat ETA
- feat(dashboard): hoist Active Agents panel above main list, show next heartbeat
- fix(dashboard): add top margin between agent list and Active Agents panel
- fix(dashboard): unstick Active Agents panel cards from "Connecting..."
- chore(release): v0.9.0
- chore: add changeset for live merge timer on task card
- fix(dashboard): keep card timer live while task is actively merging
- feat(FN-2966): reset task steps when reopening to todo or triage

Files changed:
.changeset/active-agents-live-status-and-logs.md   |  13 -
 .changeset/active-agents-no-stuck-connecting.md    |  13 +
 .changeset/active-agents-panel-hoist-heartbeat.md  |  13 +
 .changeset/dashboard-merge-base-diff.md            |  13 -
 .changeset/dashboard-task-changed-files-only.md    |  13 -
 .changeset/engine-concurrency-races.md             |  13 -
 .changeset/fix-agent-heartbeat-scheduling.md       |   5 -
 .changeset/memory-dream-session-state.md           |  13 -
 .changeset/merger-ai-summarized-commits.md         |  13 -
 .changeset/multi-node-routing.md                   |  21 -
 .changeset/pluggable-notification-providers.md     |   4 -
 .changeset/pr-mode-push-branch-and-cleanup.md      |  16 -
 .../recover-mergeable-respects-merge-settings.md   |  13 -
 .changeset/remote-tailscale-qr-tui.md              |   7 -
 .changeset/require-pr-approval-setting.md          |  17 -
 .changeset/respec-from-in-review.md                |  13 -
 .changeset/restore-task-card-fallbacks.md          |  13 -
 .changeset/tui-log-copy-feedback.md                |  13 -
 .changeset/tui-wide-stats-stack.md                 |  13 -
 CHANGELOG.md                                       | 449 +++++++++++++++++++++
 package.json                                       |   2 +-
 packages/cli-alias/CHANGELOG.md                    |  60 +++
 packages/cli-alias/package.json                    |   2 +-
 packages/cli/CHANGELOG.md                          |  44 ++
 packages/cli/package.json                          |   2 +-
 packages/core/CHANGELOG.md                         |  41 ++
 packages/core/package.json                         |   2 +-
 packages/core/src/__tests__/store.test.ts          | 127 ++++++
 packages/core/src/store.ts                         |  35 +-
 packages/dashboard/CHANGELOG.md                    |  63 +++
 .../dashboard/app/components/ActiveAgentsPanel.css |  29 ++
 .../dashboard/app/components/ActiveAgentsPanel.tsx |  35 +-
 packages/dashboard/app/components/AgentsView.tsx   |   5 +-
 .../dashboard/app/components/SettingsModal.tsx     |  13 +-
 packages/dashboard/app/components/TaskCard.tsx     |  30 +-
 .../__tests__/ActiveAgentsPanel.test.tsx           |  42 ++
 .../app/components/__tests__/AgentsView.test.tsx   |  11 +-
 .../components/__tests__/SettingsModal.test.tsx    |  68 ++++
 .../app/components/__tests__/TaskCard.test.tsx     |  36 ++
 packages/dashboard/app/sse-bus.ts                  |  13 +
 packages/dashboard/package.json                    |   2 +-
 packages/desktop/CHANGELOG.md                      |  41 ++
 packages/desktop/package.json                      |   2 +-
 packages/engine/CHANGELOG.md                       |  59 +++
 packages/engine/package.json                       |   2 +-
 .../src/__tests__/node-routing-policy.test.ts      | 102 +++--
 .../src/__tests__/scheduler-node-routing.test.ts   |  43 +-
 packages/engine/src/index.ts                       |   1 +
 packages/engine/src/node-routing-policy.ts         |  41 +-
 packages/engine/src/scheduler.ts                   |  48 +--
 packages/mobile/CHANGELOG.md                       |  41 ++
 packages/mobile/package.json                       |   2 +-
 packages/pi-claude-cli/CHANGELOG.md                |  41 ++
 packages/pi-claude-cli/package.json                |   2 +-
 packages/plugin-sdk/CHANGELOG.md                   |  57 +++
 packages/plugin-sdk/package.json                   |   2 +-
 .../examples/fusion-plugin-auto-label/CHANGELOG.md |  21 +
 .../examples/fusion-plugin-auto-label/package.json |   2 +-
 .../examples/fusion-plugin-ci-status/CHANGELOG.md  |  21 +
 .../examples/fusion-plugin-ci-status/package.json  |   2 +-
 .../fusion-plugin-notification/CHANGELOG.md        |  21 +
 .../fusion-plugin-notification/package.json        |   2 +-
 .../fusion-plugin-settings-demo/CHANGELOG.md       |  21 +
 .../fusion-plugin-settings-demo/package.json       |   2 +-
 plugins/fusion-plugin-hermes-runtime/CHANGELOG.md  |  21 +
 plugins/fusion-plugin-hermes-runtime/package.json  |   2 +-
 .../fusion-plugin-openclaw-runtime/CHANGELOG.md    |  21 +
 .../fusion-plugin-openclaw-runtime/package.json    |   2 +-
 .../fusion-plugin-paperclip-runtime/CHANGELOG.md   |  21 +
 .../fusion-plugin-paperclip-runtime/package.json   |   2 +-
 70 files changed, 1617 insertions(+), 378 deletions(-)

Fusion-Task-Id: FN-2943
2026-04-29 14:43:10 -07:00
Fusion
7cb9d20125 feat(FN-2951): enforce unavailable-node scheduling and improve active agents panel
- Enforce unavailable-node routing policy in the scheduler and wire policy integration through engine startup
- Expand scheduler and node-routing policy test coverage for unavailable-node handling and policy integration behavior
- Hoist the Active Agents panel above the main agents list and display next-heartbeat ETA details
- Fix Active Agents panel UI issues by resolving stuck "Connecting..." cards and adding spacing adjustments
- Add changesets covering Active Agents panel hoist/heartbeat ETA and connecting-state fixes

Fusion-Task-Id: FN-2951
2026-04-29 14:36:50 -07:00
Fusion
01cf62b002 feat(FN-2920): improve remote tunnel setup and heartbeat scheduling
- Add cloudflared install/detection support in remote settings API, UI, and route tests
- Surface Cloudflare tunnel prerequisites in Settings modal with remote access docs updates
- Harden heartbeat runtime scheduling by avoiding stale timeout state and simplifying runtime timeout handling
- Expand CLI/core/dashboard/engine coverage for task lifecycle, agent health, and runtime heartbeat behavior
- Add changesets for heartbeat scheduling fixes and PR approval setting updates

Fusion-Task-Id: FN-2920
2026-04-29 13:49:35 -07:00
gsxdsm
9dde067573 fix(engine): respect autoMerge and mergeStrategy in recover-mergeable-review sweep
The periodic maintenance job `recover-mergeable-review` was silently merging
in-review tasks regardless of `autoMerge` and `mergeStrategy` settings,
defeating the PR-based review flow for users with `autoMerge: false` and
`mergeStrategy: "pull-request"`.

Gate the sweep on `settings.autoMerge` (and globalPause/enginePaused for
consistency with other merge entry points) and route through the engine's
merge queue via the existing `enqueueMerge` callback so `mergeStrategy ===
"pull-request"` is honored. Falls back to the direct `store.mergeTask` path
only when no enqueue callback is wired (standalone/tests).

Closes https://github.com/Runfusion/Fusion/issues/21

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-29 13:26:05 -07:00
gsxdsm
8bbd956fa5 feat(remote): real QR codes, live tailscale URL, TUI status & shortcut
* Replace placeholder /remote/qr SVG (URL drawn as text) with real QR
  rendered via the qrcode package; add format=terminal returning ASCII
  QR for the TUI.
* Resolve the public tailscale funnel URL from captured CLI output
  instead of constructing http://<hostname>:<port> from a configured
  hostname label — that label was never used by `tailscale funnel` and
  produced a non-public URL in the auth/QR link.
* Drop hostname requirement from engine + UI; only target port matters.
* Tighten tailscale parseReadiness to require a URL on the matched line
  so the tunnel manager doesn't lock in `running` before the URL line.
* TUI: poll remote status, show ● tunnel indicator + URL in MainHeader,
  bind Ctrl+Q to a global QR overlay (terminal ASCII), and switch the
  in-Settings K shortcut to render the same ASCII QR.
* Auto-poll remote status in the dashboard while in `starting`/`stopping`
  so the UI flips to running without reopening the modal.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-29 13:20:03 -07:00
Fusion
87d9c1c380 feat(FN-2925): merge fusion/fn-2925
- Mobile header row polish: finalize QuickChatFAB CSS for mobile viewport handling, apply visual viewport height to quick chat panel, remove duplicate keyboard overlap subtraction
- Add `node-routing-policy.ts` with routing logic for mesh network nodes; expand scheduler node-routing tests with new policy coverage
- Improve reviewer decision logic and add reviewer test cases for changed behavior
- Refactor triage module (48 lines changed) with updated agent selection logic
- Update `AgentsView` component and styles; add `AgentsView.test.tsx` integration test
- Simplify `QuickChatFAB.test.tsx` test by removing CSS class permutation coverage
- Update architecture docs and settings reference to reflect new routing capabilities

Commits merged:
- fix(FN-2925): remove duplicate keyboard overlap subtraction
- feat(FN-2925): complete Step 5 — finalize mobile header row polish
- fix(FN-2925): apply visual viewport height to mobile quick chat panel
- feat(FN-2903): merge fusion/fn-2903
- feat(FN-2950): merge fusion/fn-2950

Files changed:
docs/architecture.md                               |  19 +--
 docs/settings-reference.md                         |   2 +-
 packages/dashboard/app/components/AgentsView.css   |  20 +++-
 packages/dashboard/app/components/AgentsView.tsx   |  11 +-
 packages/dashboard/app/components/QuickChatFAB.css |  32 ++++-
 .../dashboard/app/components/SettingsModal.tsx     |   4 +-
 .../app/components/__tests__/AgentsView.test.tsx   |  37 ++++++
 .../app/components/__tests__/QuickChatFAB.test.tsx |  18 +--
 .../src/__tests__/node-routing-policy.test.ts      |  78 ++++++++++++
 packages/engine/src/__tests__/reviewer.test.ts     |  19 ++-
 .../src/__tests__/scheduler-node-routing.test.ts   | 131 ++++++++++++++++++++-
 packages/engine/src/__tests__/triage.test.ts       |  16 +--
 packages/engine/src/node-routing-policy.ts         |  45 +++++++
 packages/engine/src/reviewer.ts                    |  33 ++++--
 packages/engine/src/scheduler.ts                   |  44 ++++++-
 packages/engine/src/triage.ts                      |  48 ++++----
 16 files changed, 473 insertions(+), 84 deletions(-)

Fusion-Task-Id: FN-2925
2026-04-29 12:53:10 -07:00
Fusion
fd4353f026 feat(FN-2950): merge fusion/fn-2950
- Updated routing policy documentation in `docs/architecture.md` and `docs/settings-reference.md`
- Revised docs reflect the latest routing configuration options and behavior

Commits merged:
- feat(FN-2950): complete Step 6 — update routing policy documentation

Files changed:
docs/architecture.md       | 19 ++++++++++---------
 docs/settings-reference.md |  2 +-
 2 files changed, 11 insertions(+), 10 deletions(-)

Fusion-Task-Id: FN-2950
2026-04-29 12:17:18 -07:00
gsxdsm
800c283e57 fix(engine): close executor/merger concurrency races and reviewer pause TOCTOU
FN-2910 surfaced concurrent reviewer + merger activity on the same task.
Root cause: asymmetric in-flight guards let an unpause-resume kick off a
fresh executor session while a recovery path was already running, and the
auto-merge handoff fired before the executor's finally block finished
cleanup. This sweeps the surrounding lifecycle paths for similar races and
tightens the reviewer pause gate against TOCTOU through runtime setup.

- Symmetric in-flight tracking across `executing`, `recoveringCompleted`,
  and `resumingUnpaused`; `recoverCompletedTask` bails when any are set.
- Atomic claim of the recovery slot in the completed-task watchdog before
  any awaited work.
- Workflow-rerun bounce returns "bounced" | "skipped-pending" so the
  watchdog can no longer log a false-success retry when the original
  bounce is still mid-flight.
- Self-healing's completed-task scan re-checks executing IDs inside the
  loop instead of trusting a pre-await snapshot.
- 300ms grace period before auto-merge enqueue, giving the executor's
  finally block (session disposal, child cleanup) time to drain and
  eliminating the residual log-overlap symptom from FN-2910. Test uses
  fake timers, no real sleep added.
- New AgentSemaphore.runNested for synchronously nested helper agents
  (reviewers): bumps activeCount for honest observability while bypassing
  the wait queue, preserving forward-progress fairness for the parent at
  low maxConcurrent. Both createReviewStepTool and triage's
  createReviewSpecTool now use it.
- New beforeSpawnSession hook on AgentRuntimeOptions/AgentOptions fired
  inside createFnAgent immediately before createAgentSession, past every
  awaited setup step. Reviewer wires a pause re-check that throws a
  sentinel error converted to UNAVAILABLE, closing the TOCTOU window
  where pause flipped during runtime resolution or resource loading.

All 2887 engine tests pass; engine + core + cli + dashboard + plugin-sdk
+ pi-claude-cli + desktop typecheck clean.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-29 11:26:39 -07:00
gsxdsm
91a05688ee feat(merger): generate richer merge commit messages via AI summarizer
Squash merge commits previously landed with only a bare "merge
fusion/fn-XXXX" subject and a single bullet from the branch's commit
log, leaving git log readers without insight into what actually
changed. Now buildDeterministicMergeMessage calls summarizeCommitBody
(title-summarizer lane when configured, default model otherwise) with
the step commits + diffstat, and emits a three-section body: AI summary
+ Commits merged + Files changed. The deterministic sections always
ship so AI failure / timeout still yields a substantive message.

Also extends summarizeCommitBody to take an optional commitLog and
loosens its prompt for more detail when the change warrants it.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-29 09:54:54 -07:00
gsxdsm
414cbcffda refactor(core,merger): consolidate AI commit-body summarization into ai-summarize.ts
Moves the commit-body AI helper out of merger.ts into the existing
core/ai-summarize.ts module so all short-summary AI work (titles,
chat titles, fallback merge commit bodies) shares one home with
consistent dispatch semantics, error handling, and session lifecycle.

Core (ai-summarize.ts):
- New `summarizeCommitBody(diffStat, rootDir, provider, modelId, opts)`
  exported alongside `summarizeTitle`. Same shape (provider/modelId
  args), same get-engine-or-bail dynamic loading via `getFnAgent`,
  same readonly-tools session, same disposal-in-finally pattern.
- Differs from `summarizeTitle` in three deliberate ways suited to the
  commit-body job:
    1. Returns null on any failure instead of throwing — the caller is
       always the merger, which has a deterministic fallback chain
       behind it. Throwing would force the merger to wrap every call
       in try/catch.
    2. Accepts an optional `signal` to forward engine-pause / shutdown
       cancellation, plus a configurable `timeoutMs` (default 30s)
       so a wedged AI session can't stall a merge indefinitely.
    3. Larger output ceiling (2000 chars vs title's 60) and larger
       input ceiling (4000 chars truncated diff) — commit bodies are
       multi-line and need more room than a 60-char title.
- Exported alongside `summarizeTitle` from `@fusion/core`. Constants
  (`COMMIT_BODY_SYSTEM_PROMPT`, `MAX_COMMIT_BODY_INPUT_LENGTH`,
  `MAX_COMMIT_BODY_LENGTH`, `DEFAULT_COMMIT_BODY_TIMEOUT_MS`) re-exported
  for callers that want to override behavior.

Engine (merger.ts):
- Dropped the local `aiGenerateCommitBody` function (~70 lines) — it
  duplicated the session-creation pattern from `summarizeTitle` while
  living in a place where future maintainers wouldn't think to look.
- `resolveSafeCommitBody` now imports `summarizeCommitBody` from
  `@fusion/core` and delegates. The cascade behavior is unchanged
  (commitLog → AI → diff stat → synthetic) and the title-summarizer
  model preference is preserved (provider/modelId resolved here, then
  passed through).

Tests:
- 6 new test cases in `ai-summarize.test.ts` covering:
  empty input → null, missing engine → null (graceful, never throws),
  missing engine + model selection → null, pre-aborted signal → null,
  custom timeout (returns quickly under 1s ceiling), exposed constants.
- Core: 3136/3136 pass (was 3130 — +6 new). Engine: 2887/2887 pass.
- Typecheck clean, workspace lint clean.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-29 08:30:34 -07:00
gsxdsm
5c2240caab feat(merger,dashboard): use title-summarization model for commit body AI + reword settings
Wires the AI commit-body generator (introduced in 52928ff5e) to the
existing dedicated title-summarization model lane, and updates the
settings UI so users understand the model is now used for two
short-summary jobs instead of just one. Also de-duplicates the
project-scope settings UI which was exposing the same model setting in
two places.

Engine (merger.ts):
- aiGenerateCommitBody now prefers settings.titleSummarizerProvider /
  titleSummarizerModelId over the merger's default model. The
  summarization lane is the right tier for this work — small, fast,
  cheap. Falls back to the default merger model when the summarization
  lane is unset.

Dashboard (SettingsModal.tsx):
- MODEL_LANES summarization lane label updated:
  "Title Summarization Model" → "Title and Git Commit Message
  Summarization Model". Helper text updated to mention the dual purpose
  (auto-generated task titles + fallback merge commit message bodies).
  This change flows through automatically to BOTH the global model
  lanes view and any project-scope rendering of MODEL_LANES.
- Removed the duplicate summarization picker from the project-scope
  Model Lanes section: previously it appeared once under Model Lanes
  AND once under "AI Summarization". Now lives only in the dedicated
  picker so users have a single source of truth in project scope.
- The dedicated picker section heading + description rewritten:
  "AI Summarization" → "AI Title and Git Commit Message Summarization",
  with a paragraph explaining both jobs the model performs.
- Inner dropdown label updated for consistency.

Tests + checks: engine 2887/2887 pass, dashboard typecheck clean,
workspace lint clean.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-29 08:22:04 -07:00
gsxdsm
52928ff5e7 fix(engine): guarantee non-empty merge commit body via AI fallback cascade
Audit of merge commit sites surfaced four paths that could produce an
empty commit body when the branch had no unique commits relative to its
base (so `git log <base>..<branch>` returns empty), or when the `git log`
collection itself failed. Subject was always non-empty so git accepted
the commit, but the body was missing — breaking downstream consumers
(release notes, dashboard summaries, mergeDetails) that read it.

The AI merge agent is the primary author of merge commit messages; these
fallback paths only run when the agent didn't commit and the merger has
to commit on its behalf. Previously they used `-m "${commitLog}"` which
silently produced `-m ""` on empty input.

Now uses a 4-tier resolveSafeCommitBody cascade — most informative
first, with a deterministic floor so the function never returns empty
and never throws:

  1. The branch's commit log if non-empty.
  2. AI-generated body via aiGenerateCommitBody — a fresh readonly
     session that summarizes the diff stat into 2–6 bullet points.
     Bounded by a 30s timeout (forwards the caller's abort signal too)
     so engine pause / shutdown tears it down promptly. Any failure
     falls through.
  3. The diff stat itself, formatted as a "Files changed" listing.
  4. A synthetic `- merge <branch>` placeholder.

Wired into all three merger fallback commit sites:
  - Auto-resolved-conflicts commit (Attempt 2 success path)
  - -X ours / -X theirs side-strategy commit (Attempt 3)
  - Agent-didn't-commit fallback commit (post-AI verification)

Also defensive: removed `--allow-empty-message` from the executor's
squash-import commit. The message is hardcoded non-empty (subject +
body), but the flag was a footgun — git would silently accept an empty
message if the construction ever broke. Switched to two `-m` args
(subject and body separately) so empty would now correctly fail at
git's level rather than silently land a blank-message commit.

Tests + checks: engine 2887/2887 pass, typecheck clean, workspace lint
clean.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-29 08:19:18 -07:00
gsxdsm
42c27fcc3e fix(FN-XXX): harden windows path handling 2026-04-29 07:35:23 -07:00
gsxdsm
b1d2a9ee43 fix(engine): tighten Layer 3 — pass safety constraint into AI prompt + harden Layer 2 restore
Self-review of the recovery cascade surfaced three issues; this commit
addresses all of them.

1. AI didn't actually receive the safety constraint under Layer 3.

The previous commit logged the safety preamble to the task log via
`store.logEntry`, but the merge agent doesn't read task log entries as
prompt context — so the AI was running blind. The "no silent
re-introduction of main's deletions" guarantee was therefore relying
*entirely* on the deterministic verification gate (test + build),
which is correct as a backstop but doesn't help the AI produce a
correct first attempt.

Fixed by threading `preMergeRebaseFallthrough` through
`MergeAttemptParams` → `executeMergeAttempt` → `runAiAgentForCommit` →
`MergePromptParams` → `buildMergePrompt`, where it now injects an
explicit "⚠️ Pre-merge rebase recovery exhausted" preamble at the top
of the user prompt with three concrete rules:
  - Prefer main's deletion when branch re-adds removed lines
  - Prefer main's version on ambiguous hunks
  - Call `fn_report_build_failure` rather than commit a regression
Also includes the original rebase failure message (truncated) so the
AI has diagnostic context.

The truncated-context retry path also forwards the preamble — it's the
safety constraint, not bulk context, so we keep it even when stripping
diff stat / commit log to fit the window.

2. Layer 2's branch-restore could fail with "uncommitted changes".

When a cherry-pick midway through Layer 2's replay fails, the worktree
is in a half-applied state with conflicts in the index. The previous
restore did `git checkout <branch>` (no -f) followed by
`git reset --hard <originalSha>`. The plain checkout would refuse with
"would overwrite local changes" if there were unmerged paths,
preventing the reset from running and leaving the branch at the
half-replayed tip.

Fixed by reordering: hard-reset to the captured original SHA first
(this clears index/working tree of any cherry-pick state), then
`git checkout -f <branch>` to ensure HEAD points at the named branch,
then a final hard-reset to the original SHA as belt-and-suspenders.
Worst case the worktree is at the original branch tip — never worse
than where Layer 2 started.

3. Pre-existing unrelated lint error blocking workspace lint.

`packages/dashboard/src/server.ts` had an unused `resolve` import from
`node:path` left behind by a recent refactor that extracted
`PACKAGE_VERSION` into its own file. The user explicitly asked to
clean it up so workspace lint passes. One-line drop.

Tests + checks:
- Engine: 2886 / 2886 pass (added safety preamble didn't break any
  existing prompt-content assertions)
- Core: 3120 / 3120 pass
- Workspace lint: clean
- Engine typecheck: clean

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-29 07:20:20 -07:00
Fusion
ba3d868c2d feat(FN-2944): merge fusion/fn-2944
- test(FN-2944): cover already checked out worktree conflict recovery
- fix(FN-2944): recognize git already checked out worktree conflict
- fix(engine): auto-recover from squash-merge orphan rebase failures

Fusion-Task-Id: FN-2944
2026-04-29 07:10:52 -07:00
gsxdsm
a74b77cec5 fix(engine): auto-recover from squash-merge orphan rebase failures
Adds a layered recovery cascade to the merger's pre-rebase stage so tasks
no longer get stuck in in-review when their declared dependency was
squash-merged to main and left orphan raw commits in the dependent's
history. Also prevents the orphan situation at the source for new tasks.

Why:
- 13 tasks were stuck in in-review for hours, all hitting the same
  pre-merge rebase abort because they shared 6 raw commits inherited
  from FN-2729's branch (declared baseBranch). FN-2729 was then
  squash-merged to main, turning those raw commits into orphans whose
  content is in main but in a different commit shape, conflicting with
  later-merged tasks. The merger's `smart-prefer-main` strategy
  correctly refused -X ours (which would silently re-introduce main's
  deletions), but the only escape hatch was a 30-min cooldown loop
  that retried the same impossible rebase forever.

Recovery cascade (merger.ts pre-rebase stage):
- Layer 1: surgical `git rebase --onto <main> <dep-tip> <branch>` when
  task.baseBranch is set. Resolves the dep tip from the live branch ref
  or recorded baseCommitSha; peels off the dep's inherited commits
  cleanly. Captures the squash-merge-of-dep case end-to-end.
- Layer 2: generic patch-id duplicate-content stripping. Walks the last
  500 main commits, computes patch-ids, then drops branch commits whose
  patch-id matches and cherry-picks the remainder onto main. Captures
  manual cherry-picks, double-merges, and any other duplicate-content
  variant Layer 1 doesn't see. Restores the branch's pre-mutation SHA
  on partial-failure so worst case leaves the worktree no worse than
  before the recovery attempt.
- Layer 3: AI arbitration fall-through. If Layers 1+2 fail, log the
  situation and proceed to the existing 3-attempt AI merge cascade
  instead of throwing. The deterministic post-merge verification
  (test + build) gates whatever the AI produces — that gate is what
  enforces prefer-main's safety contract under fall-through (no silent
  re-introduction of main's deletions).
- Critical: the unsafe `-X ours` Attempt 3 is suppressed under
  fall-through. AI Attempts 1+2 are the only paths that can complete
  the merge; if both fail and verification rejects them, the task
  bounces back to in-progress via the existing engine path rather than
  silently merging.

Prevention (executor.ts worktree creation):
- When a task declares a non-main `baseBranch`, branch the worktree
  off main (origin/<defaultBranch> when worktreeRebaseBeforeMerge is
  enabled and a remote is resolvable; otherwise local rootDir HEAD)
  and `git merge --squash` the dep's content as a single import commit.
  The dependent branch then carries main's history + 1 commit instead
  of inheriting the dep's raw commits, so a future squash-merge of the
  dep produces patch-id-matching content that rebases cleanly.
- Honors settings: respects `worktreeRebaseBeforeMerge`,
  `worktreeRebaseRemote`, and falls back to local HEAD when no remote
  is resolvable. Fully fail-soft: any squash-import error falls back to
  the legacy fork-from-dep behavior so worktree creation still works
  for setups where the squash flow can't run.

Engine-side last-retry fix (project-engine.ts):
- Changed conflict-retry condition from `currentRetries < MAX` to
  `currentRetries + 1 < MAX` so the bounce-to-in-progress code fires
  in the same engine tick as the failing attempt, rather than relying
  on a setTimeout-scheduled Nth attempt that dies on engine restart.
  Without this, a dev-time engine restart between the 3rd and 4th
  retry left the task with mergeRetries=MAX and only the 30-min
  cooldown sweep could try again.

Tests:
- New "Layer 1 recovery" test asserts the surgical --onto rebase fires
  when baseBranch is set and primary rebase aborts, and that Layer 3
  fall-through is NOT triggered when Layer 1 succeeds.
- Updated the "no silent fall-through to -X ours" test to cover the
  new fall-through path: even after Layers 1+2 fail and the merge
  cascade proceeds, -X ours must not run, and the task log must record
  both the Layer 3 fall-through entry and the Attempt 3 suppression.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-29 07:06:13 -07:00
gsxdsm
14d2bb7b8e fix(engine): prevent auto-merge cooldown loop on unresolvable conflicts
Tasks were getting stuck in `in-review` forever when auto-merge could not
resolve conflicts within MAX_AUTO_MERGE_RETRIES. The conflict-exhaustion
branch silently cleared `status` (no error, no log entry, no comment),
and the 30-min cooldown sweep would reset retries and re-attempt the
same impossible merge — looping silently with no user-facing surface.

Why:
- FN-2918 and FN-2903 both spent hours in this loop with no error/comment
  visible on the task. The only log evidence was repeated
  "Auto-merge retry cooldown elapsed (30m idle)" entries with no
  follow-up outcome.

How to apply:
- Every merge failure now writes a `<Manual|Auto>-merge failed: <msg>`
  entry to the task log so the dashboard surfaces the reason.
- Conflict-retry exhaustion now bounces the task back to `in-progress`
  with a comment + log entry so the executor re-rebases against main
  and retries — mirroring the verification-failure-bounce pattern.
- New `mergeConflictBounceCount` task field caps outer bounces
  (`MAX_MERGE_CONFLICT_BOUNCES = 2`); past the cap, the task is parked
  in `in-review` with `status="failed"` and a follow-up triage task is
  created so a human can resolve the conflict manually.
- Non-conflict and non-direct-strategy errors now also set
  `status="failed"` so the cooldown sweep can't re-pick them up.
- `canMergeTask` skips tasks with `status="failed"` so terminal
  failures (verification cap, bounce cap, non-conflict error) are no
  longer eligible for cooldown re-attempts.

Schema migration v52 adds the `mergeConflictBounceCount` column.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-28 23:17:10 -07:00
Fusion
6bb919864b feat(FN-2895): merge fusion/fn-2895
- feat(FN-2895): complete Step 7 — documentation and delivery

Fusion-Task-Id: FN-2895
2026-04-28 21:50:27 -07:00
Fusion
9286205f4b feat(FN-2915): merge fusion/fn-2915
- feat(FN-2915): include github issue refs in commit workflows
- refactor(dashboard): simplify setup wizard manual step
- fix(engine): prevent phantom merges when verification fix runs without a commit

Fusion-Task-Id: FN-2915
2026-04-28 21:13:30 -07:00
gsxdsm
98c5736d22 fix(engine): prevent phantom merges when verification fix runs without a commit
When the AI merge agent reported a build failure via fn_report_build_failure,
executeMergeAttempt immediately ran `git reset --merge` and threw. The catch
handler then spawned an in-merge fix agent on a clean main and called
amendMergeCommitWithFixes, which blindly amended HEAD — the *previous* task's
merge commit — silently dropping the current task's branch and inheriting
the prior task's stats. The dashboard then reported the new task as merged
with completely unrelated files.

- Drop the immediate reset at the build-failure throw site so the squash
  state survives for the in-merge fix path.
- Capture preAttemptHeadSha at each mergeAttempt and refuse to amend when
  HEAD never moved past it; instead, create a fresh commit from the squash
  + fix changes. If neither HEAD moved nor anything is staged, abort the
  merge instead of fabricating success.
- Move the cleanup reset into the mergeAttempt catch handler (with a
  labeled resetMergeWithWarn helper) so it still fires when the fix path
  is exhausted or disabled.
- Replace the AI-authored commit body with a deterministic body built from
  the branch's actual step-commit subjects after every successful AI merge.
  Stops the recurring problem of merge messages describing files that are
  not in the diff.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-28 21:04:01 -07:00
gsxdsm
3f061460b9 fix(FN-2662): honor project model overrides and stabilize tests 2026-04-28 18:21:17 -07:00
gsxdsm
e8e6698038 fix(engine): zero out file stats on empty-merge mergeDetails
Follow-up to 502fddf20: when the merge was empty (no commit made),
filesChanged/insertions/deletions were still being captured from
git show --shortstat HEAD — which describes pre-merge HEAD's commit,
unrelated to this task. Consumers (dashboard, audit log) would render
those numbers next to "no commit landed", which is misleading.

Clear stats to 0 alongside the omitted commitSha. Also drop the stats
line from the agent-log summary when mergeWasEmpty.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-28 17:39:59 -07:00
gsxdsm
439ea6320a feat(engine): surface merger activity on the agent-log timeline
Users couldn't see the merger's per-task activity from the dashboard
agent-log view — only the executor's session output was visible. When a
merge took an unexpected path (rebase ran twice, attempt 2 auto-resolved
3 lockfiles, attempt 3 fell back, etc.) the only record was in process
logs, which most users don't have access to.

Add appendAgentLog calls at the high-signal merge events:

- Pre-merge rebase: when each stage (remote → remoteRef, local-base
  → local HEAD) completes successfully
- Each merge attempt start, with attempt number + strategy summary
- Final merge outcome: strategy, attempt count, commit sha, file stats,
  and edge cases (empty merge, deferred sha)

Source attribution uses "merger" so the dashboard can style/filter
these distinct from the executor's per-step messages.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-28 17:33:48 -07:00
gsxdsm
9c48fd57a4 fix(engine): make stale-merge recovery robust to includeTaskIdInCommit=false
recoverInterruptedMergingTasks searched for landed commits by grepping
commit subjects for the task ID. Users with includeTaskIdInCommit=false
have commit subjects like `feat: ...` (no task ID), so if the merger
crashed after committing but before storing mergeDetails, recovery would
silently fail to find the commit and incorrectly retry the merge.

Three layered defenses:

1. Emit a Fusion-Task-Id: <id> trailer in every Fusion-managed merge
   commit body. The 4 fallback commit invocations now include
   `-m "Fusion-Task-Id: ..."`. After the AI agent commits, an
   idempotent ensureTaskIdTrailerOnHead() amends the trailer in via
   `git interpret-trailers` (no-op if already present).

2. findLandedTaskCommit now tries three sources in order:
   a. task.mergeDetails.commitSha (if reachable from HEAD)
   b. Fusion-Task-Id trailer grep (anchored regex)
   c. Subject grep (legacy commits)

3. Trailer grep uses an anchored regex `^Fusion-Task-Id: <id>$` so
   it doesn't false-match task IDs appearing as substrings.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-28 17:32:30 -07:00
gsxdsm
502fddf20e fix(engine): keep mergeDetails.commitSha honest on empty/post-push paths
Two cases where mergeDetails.commitSha was wrong:

1. Empty-squash success paths (mergeAttempt + attemptWithSideStrategy
   return true when nothing was staged) recorded pre-merge HEAD as the
   task's commitSha. That commit had nothing to do with this task —
   misleading the dashboard, audit log, and recovery scans.

2. pushAfterMerge can trigger an internal pull --rebase that rewrites
   HEAD; mergeDetails was captured before push, so the stored sha
   referenced a now-orphaned commit.

Fix:
- Track an empty-merge flag on AiInvocationTracker, set at the three
  squashIsEmpty/staged===0 sites. Metadata block omits commitSha when
  the flag is set.
- After successful pushAfterMerge, recapture HEAD and update
  mergeDetails.commitSha if it changed.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-28 17:29:21 -07:00
gsxdsm
6d9dcdd755 fix(engine): split pre-merge rebase into independent remote + local-base stages
The pre-merge rebase had the local-base rebase nested inside the
remote-rebase success path, so when no remote resolved (or worktreePath
was missing), the entire block exited without running local-base. That
left smart-prefer-main exposed: with no rebase, the -X ours fallback
would silently re-introduce code main had recently deleted, which is
exactly the case the strategy is meant to prevent.

Restructure so remote rebase (Stage 1) and local-base rebase (Stage 2)
run as independent gates. Local-base rebase still picks up sibling-task
merges that landed locally even when the remote stage was skipped or
disabled, so prefer-main always gets at least one defense.

Also relax the semantic-incompatibility guard: prefer-main now requires
EITHER stage to remain enabled (was: required worktreeRebaseBeforeMerge).

Extracted runLocalBaseRebase() helper to remove duplication between the
two entry points (after Stage 1 vs. standalone) and centralize the
ancestor-check + abort handling.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-28 17:24:12 -07:00
gsxdsm
07a11437c0 fix(engine): paused-task guard for merged + misclassified review recovery
Two recovery scans operated on in-review tasks without checking
!task.paused:

- recoverMergedReviewTasks would move a paused task whose merge was
  already confirmed to done, against user intent.
- recoverMisclassifiedFailures would clear the error on a paused failed
  task, defeating the user's intent to investigate manually.

Add the paused guard to both, matching the pattern used by all other
recovery scans. Completes the pause-vs-stuck audit.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-28 17:20:04 -07:00
gsxdsm
9d09b4c599 fix(engine): refuse to treat non-conflict squash failures as no-op merges
Attempt 2 of the merge cascade caught any git merge --squash failure into
mergeExitedWithConflicts=true. If the failure was non-conflict (pre-commit
hook rejection, IO error, locked repo) and produced no U files, the code
fell into the "all conflicts auto-resolved" branch with empty classified
arrays, ran deterministic verification on pre-merge HEAD, and returned
true — recording merge metadata for a merge that never happened.

Distinguish "exit code 1 with U files" (recoverable) from "any other
failure" (real). When a real failure surfaces with no conflicts, raise a
sentinel MergeNonConflictError that the outer mergeAttempt catch propagates
without retrying — retrying just re-runs the same broken command.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-28 17:19:12 -07:00
gsxdsm
a0f9e9019e fix(engine): skip paused tasks in interrupted-merge recovery
recoverInterruptedMergingTasks was the one self-healing scan that didn't
guard !task.paused. A user who paused a task mid-merge (status=merging,
column=in-review) would still see the recovery scan finalize or unblock
the merge once the stuck timeout elapsed, violating their pause intent.

The other 12 self-healing recovery scans either explicitly skip paused
tasks or operate on terminal/metadata-only state where pause is moot.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-28 16:59:55 -07:00
gsxdsm
8aaa479296 fix(engine): harden smart-prefer-main against silent rebase skips
The smart-prefer-main strategy depends on a successful pre-merge rebase
to honor main's deletions. Previously, three failure modes silently fell
through to the -X ours merge, which would re-introduce code main had
just removed (because branch additions vs main deletions don't textually
conflict and -X ours only resolves content conflicts, not modify/delete).

- Hard-fail when prefer-main is paired with worktreeRebaseBeforeMerge=false
  (semantically incoherent combination)
- Hard-fail when the pre-merge rebase starts and aborts (any of the three
  rebase paths: remote, nested local-base, or fallback local-only)
- Warn (not throw) on environmental silent skips — no remote resolvable
  or no worktreePath — so the gap is observable in logs without breaking
  common test/setup environments

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-28 16:57:17 -07:00
gsxdsm
ad45c0b1b4 feat(core,engine): split smart merge strategy into prefer-main / prefer-branch
The single "smart" strategy is now two flavors with the new default flipped
to prefer-main. Both share a pre-cascade `git fetch origin <currentBranch>`
+ best-effort fast-forward so a freshly-pushed sibling commit doesn't get
clobbered when the fallback resolves a conflict against a stale base.

- "smart-prefer-main" (new default): -X ours fallback. Protects just-merged
  sibling work from being regressed by a concurrent task branch.
- "smart-prefer-branch": -X theirs fallback. Equivalent to legacy "smart".

Legacy "smart" / "prefer-main" enum values are accepted and normalized via
`normalizeMergeConflictStrategy()` so existing settings.json files migrate
seamlessly. The fast-forward step gracefully degrades on fetch failure or
divergent local main (logs and continues).

Updates settings UI dropdown, test helpers, and adds 5 fetch+ff regression
tests + 7 normalize-helper tests. Lint cleanup of two empty catch blocks
in scripts/release.mjs.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-28 15:44:38 -07:00
Fusion
69a5cd4ab8 feat(FN-2869): merge fusion/fn-2869 (auto-resolved)
- docs(FN-2869): complete Step 3 — update notification architecture docs
- docs(FN-2869): complete Step 2 — document notification providers settings
- chore(FN-2869): add changeset for pluggable notification providers
2026-04-28 15:20:49 -07:00
Fusion
5e6dbbb6f3 fix(dashboard): hand off cleanly from setup wizard to model onboarding
On a fresh install useAuthOnboarding's effect ran at mount before the
setup wizard's 500ms auto-open timer fired. The one-shot ref locked,
and the resolved fetch could either stack model onboarding on top of
the wizard or never re-trigger after the wizard closed.

- Gate the trigger on projectId being set so the wizard owns the
  bootstrap phase; the auth check only fires once a project exists.
- Re-check setupWizardOpen via a ref when the auth fetch resolves to
  avoid stacking onboarding on top of a wizard opened mid-fetch.
- Release the one-shot in that suppressed branch so the effect retries
  when the wizard closes.

Adds two regression tests: fresh-install handoff and mid-fetch wizard
suppression.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-28 15:14:18 -07:00
gsxdsm
f9397f9670 fix(engine): honor project execution model overrides in spawned children + workflow-step timeout fallback
Two related executor fixes:

1. Spawned child agents previously bypassed the executor model lane hierarchy
   and used settings.defaultProvider/defaultModelId directly, ignoring
   project-level executionProvider/executionModelId from .fusion/config.json.
   Resolve via resolveExecutorModelPair() so children honor the same
   precedence as the parent executor.

2. Pre-merge workflow step AI calls now have a wall-clock timeout
   (settings.workflowStepTimeoutMs, default 6 min) and fall back to the
   configured validatorFallback / fallback model on timeout. The 20-min
   stuck-detector kill loop was the only escape hatch when a provider's
   streaming API hung mid-response, and the kill triggered a same-provider
   retry — guaranteeing repeat hangs. The runner now races the prompt against
   a timeout; on timeout it disposes the session, logs a clear entry, and
   re-runs the step once with a distinct fallback provider/model. If neither
   completes (or no fallback is configured), the step returns a normal
   failure that flows into the existing handleWorkflowStepFailure retry path.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-28 14:25:56 -07:00
gsxdsm
d7b5250a22 fix(merge): clear mergeActive + abort wedged session on stale-merge recovery
The prior commit re-enqueued tasks after stale-merge recovery but didn't
account for the engine's in-memory `mergeActive` set, which still held the
wedged task. `internalEnqueueMerge` silently no-ops when the entry is
present, so the re-enqueue had no effect.

The recovery callback now also aborts the active merge's signal and
disposes its session if the wedged attempt was the currently-active one.
This is what unsticks tasks where an AI provider call is hung mid-await
and the surrounding `try/finally` never gets a chance to run.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-28 14:25:56 -07:00
gsxdsm
577ec0a66f fix(merge): re-enqueue stale merges + rebase new worktrees onto remote
Stale-merge recovery now calls back into ProjectEngine's auto-merge queue
directly instead of waiting on the 15s polling sweep — wired via a new
InProcessRuntime.setMergeEnqueuer hook so SelfHealingManager can re-enqueue
without leaking engine internals.

createWorktree mirrors the merge-time rebase: when worktreeRebaseBeforeMerge
is enabled, the new task branch is rebased onto <remote>/<defaultBranch>
right after creation, so executors start from origin's tip with local main
replayed on top. Best-effort — fetch/rebase failures abort cleanly and
leave the merge-time rebase as the backstop.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-28 14:25:56 -07:00
Fusion
78836b1969 fix(FN-2883): reset merge state when re-entering in-progress
- Reset merge metadata, verification counters, and workflow results when tasks move from in-review/done back to in-progress
- Reopen verification-related steps (or the last step fallback) so re-verification runs from a pending state
- Add execute-time guard to clear stale mergeDetails on in-progress tasks before continuing
- Prevent resumeOrphaned fast-path recovery when completed in-progress tasks still carry merge metadata
- Add targeted executor and TaskForm tests covering FN-2883 regression paths
2026-04-28 14:25:56 -07:00
Fusion
8d606b45c5 feat(FN-2878): add webhook notification provider support
- Add webhook settings fields and defaults for enablement, URL, format, and event filtering
- Implement WebhookNotificationProvider with payload formatting support for generic, Slack, and Discord endpoints
- Extend NotificationService to manage both ntfy and webhook providers with live settings sync
- Export webhook notification types/providers through engine notification entry points
2026-04-28 14:25:56 -07:00
Fusion
e782636c4b feat(FN-2866): wire provider-backed notification service into engine
- Add notification service module with provider abstractions and ntfy provider implementation
- Refactor NtfyNotifier into a compatibility wrapper that delegates task-event delivery to NotificationService
- Initialize and stop NotificationService from ProjectEngine while preserving gridlock notifications via NtfyNotifier
- Export notification APIs from engine index and add focused unit coverage for provider, service, and project-engine wiring
2026-04-28 14:25:56 -07:00
Fusion
5ab5e5467e fix(FN-2872): reduce blocked-task log noise and clarify routing details
- Add a Node Routing section in TaskDetailModal showing override, effective node/source, unavailable-node policy, and blocking reason
- Remove duplicate blocked-state heartbeat log output when the blocked reason has not changed
- Silence runtime onBlocked logging to avoid repeated blocked-task log spam
2026-04-28 14:25:56 -07:00
gsxdsm
54f4fd540e feat(FN-2862): merge fusion/fn-2862 2026-04-28 14:25:55 -07:00