The merge delivers three major features: a droid CLI path reconciliation extension that resolves workspace path mismatches for spawned agents, session-first quick chat with improved heartbeat prompts and a dramatically simplified QuickChatFAB component, and canonical agent asset directory naming wit
Fusion-Task-Id: FN-3033
The merge introduces a new droid CLI extension with path reconciliation across daemon, dashboard, and serve commands, along with supporting core and engine utilities. It also removes stale lint suppression comments (FN-2983). Tests cover the new CLI extension and path reconciliation logic.
Fusion-Task-Id: FN-2984
Merges FN-2982 Step 3: adds model filtering for the droid-cli settings API, fixes planning mode question display and new-session reset in the dashboard, and adds test coverage for droid-cli settings and model route registration.
Fusion-Task-Id: FN-2982
Migrations 56 (chat_sessions.cliSessionFile) and 57 (ai_sessions.archived)
were added without bumping SCHEMA_VERSION, so migrate() early-returned on
existing v55 DBs. The dashboard surfaced this as repeated 500s on
/api/ai-sessions: "no such column: archived". Add a guardrail test that
parses db.ts and asserts SCHEMA_VERSION matches the highest applyMigration
target.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This merge lands v0.12.0 with two major features: a droid-cli provider integration adding auth routes, status endpoints, and a settings toggle hook for controlling CLI-based authentication, plus a new experimental agent onboarding modal with a create-agent form. The release also stabilizes engine st
Fusion-Task-Id: FN-2981
The title summarizer ran with `tools: "readonly"` but host extensions
(`@runfusion/fusion`) were still injected, exposing `fn_task_create` and
the rest of the `fn_*` mutation surface. A summarizer model called
`fn_task_create` mid-summary, spawning an unintended sibling task and
leaving its chat-style reply ("Created **FN-xxxx** with the full spec…")
sliced as the original task's title.
- pi.ts: in `tools: "readonly"` mode, skip host extension paths and drop
caller-supplied customTools so the session truly only has read/grep/
find/ls.
- ai-summarize.ts: harden all four system prompts (title, merge summary,
commit body, commit subject) with explicit no-tool / treat-input-as-
content framing; wrap the title prompt's user content in a
`<description>` delimiter; route the AI response through new
`sanitizeTitle` that strips chatty preambles, markdown emphasis,
surrounding quotes, and trailing punctuation before truncation.
- Tests: add a regression covering the exact incident shape plus
unit coverage for `sanitizeTitle` edge cases.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Added a new `droid-cli-probe` module to the dashboard package with test coverage, implementing a CLI probe capability for the droid system.
Fusion-Task-Id: FN-2979
- Add backend onboarding session service and legacy API routes for start, state, and import/export generation
- Introduce experimental AgentOnboardingModal UI with stream-state handling, model selector wiring, and onboarding launch flow in AgentsView
- Add onboarding gating via prompt overrides/store updates and include search behavior fix for partial-match filtering
- Expand dashboard/core tests for onboarding API, modal behavior, agents/settings integration, and prompt override handling
Fusion-Task-Id: FN-3025
Merges FN-3009 (remote settings migrated to global scope with updated schema and docs), FN-3034 (legacy routines compatibility layer: agentId backfill on load and schema validation for old format), and FN-3039 (Nerd Font glyph asset bundled into the terminal with CSS prioritization). Core settings s
Fusion-Task-Id: FN-3034
This merge migrates Fusion's settings architecture to a unified global scope (FN-3009), consolidating settings types, defaults, merge semantics, updater logic, and routing into a centralized system that aligns dashboard remote settings with global configuration. Secondary changes include adding the
Fusion-Task-Id: FN-3009
Exports the research settings resolver from `@fusion/core` types to fix dashboard alias builds, ensuring the resolver is available when imported through the dashboard's module alias configuration.
Fusion-Task-Id: FN-2995
The vitest child-process guard wrapped exec/execFile without preserving the
`[util.promisify.custom]` symbol, so awaited `execAsync` resolved to a raw
stdout string instead of `{stdout, stderr}`. That single regression cascaded
through ~60 "failing" tests across cli, core, engine, and dashboard whose
production code was actually correct. Also relax the AI-CLI blocklist for
cheap introspection (--version/--help/which …), give SIGTERM'd subprocesses a
brief grace period before being flagged as "left running", fix a few real
test-side bugs uncovered along the way (executor mock step transitions, iOS
last-resort keyboard path, mission SSE replay tests racing with the real AI
agent), and convert dashboard route tests' dynamic `await import("../server.js")`
to static imports so first-test timings drop from 2–5s to <200ms.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Workflow-step REVISE retries, pause→todo handoffs, and the
context-overflow fresh-session requeue were all routing tasks back to
`todo` before returning to `in-progress`. The default reopen-to-todo
path reset every step to pending and rewrote PROMPT.md checkboxes, so
each retry restarted from step 0 even when earlier steps had already
been done — the symptom seen on FN-2978, where every workflow REVISE
or pause cycle wiped the task's progress.
- Add `preserveResumeState` to `TaskStore.moveTask`. When set, skip
`resetAllStepsToPending` + `resetPromptCheckboxes` and keep
`worktree` and `executionStartedAt` so the resumed run reattaches to
the same checkout. `status`, `error`, and `blockedBy` still clear.
- Use it on the workflow-rerun bounce, the three pause-graceful
handoffs, and the context-overflow requeue. The agent-terminated
pause path still discards (it nukes worktree+branch by design).
- Context-overflow requeue clears `sessionFile` synchronously in the
awaited `updateTask` immediately before `moveTask`, so the next
dispatch cannot reopen the saturated session via a stale pointer.
- `fn_task_update` no longer silently regresses `done`/`skipped` steps
to `in-progress`, no longer captures a stale rewind checkpoint when
it does, and tells the agent honestly when a regression is ignored.
- Mobile chat keyboard: ChatView/QuickChatFAB gate layout on the new
`keyboardOpen` flag so focused-input + viewport-shrink iOS cases
still adjust when the computed overlap is zero.
Tests:
- New `preserveResumeState` coverage in store.test.ts; updated
workflow-rerun + pause-graceful assertions in executor.test.ts.
- Restructured the previously-flaky "routes exhausted prompt-mode
workflow hard failures" test to drive the bounce inline; passes in
isolation and in the wider workflow/pause/context sweep (59/59).
- Added regression tests in ChatView.test.tsx and QuickChatFAB.test.tsx
for the iOS last-resort `keyboardOpen=true, keyboardOverlap=0` case.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
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>
- Reset all task step statuses to pending and set currentStep to 0 when moving from in-progress, in-review, or done back to todo/triage
- Reset checked boxes in PROMPT.md on reopen so execution checklists match the reset step state
- Keep reopen cleanup scoped to reopen transitions while preserving existing behavior for non-reopen moves
- Add targeted store tests covering reopen paths, prompt checkbox reset, and no-op behavior for tasks without steps
Fusion-Task-Id: FN-2966
Add `triage` to VALID_TRANSITIONS["in-review"] so the dashboard's
`Request AI Revision` and `Rebuild Spec` actions work for in-review
tasks. moveTask now applies the same full reset on in-review → triage
as on in-review → todo (clears branch/baseBranch/baseCommitSha/summary/
recovery metadata and workflowStepResults) so the respec'd task starts
from scratch. The in-review task card's Move menu also gains Planning
as a destination.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- Expand core test coverage for task node overrides, unavailable node policies, and defaultNodeId settings contracts
- Restrict dashboard task change views to files actually modified by the task, with updated API route handling and tests
- Improve quick chat UX and styling with extended component behavior and hook updates
- Harden memory dream extraction by reading assistant text from session state and covering undefined-output cases
- Enhance merger commit summarization and update architecture/settings/multi-project/task-management docs
Fusion-Task-Id: FN-2840
Update dashboard mocks to push assistant text into session.state.messages
(matching real session.prompt() void return) and add a regression test for
array-shaped content. Add core unit tests for extractDreamProcessorResult
to lock in undefined/null safety.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
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>
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>
Audit of task.status assignments across packages/core and packages/engine
surfaced five additional transient/in-flight statuses that should block
auto-merge but weren't in BLOCKING_TASK_STATUSES:
- awaiting-approval — triage spec awaiting user approval
- needs-replan — scheduler/executor/triage signaled re-plan needed
- mission-validation — mission-level validation in flight
- queued — scheduler-side transient state
- stuck-killed — defensive guard; task killed by stuck detector
Each represents a state where finalizing the merge violates user intent
or invariants. Group the set with comments explaining the intent of each
group so future additions can be slotted appropriately.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
A task in column "in-review" but with status "planning" (or the legacy
"specifying" alias) was passing the merge eligibility check, so the
auto-merger would finalize a branch the user had just moved back to
re-plan. The hardcoded BLOCKING_TASK_STATUSES set didn't include either
status — only failed / awaiting-* / merging-* were checked.
Add both to the set so getTaskMergeBlocker reports them as blockers,
matching the expected user mental model of "moved to planning = won't
auto-merge".
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- Add Database APIs to rebuild the tasks_fts index and detect FTS5 corruption signatures
- Retry task upserts once after rebuilding FTS5 when corruption errors are encountered
- Extend TaskStore health checks to include FTS5 integrity verification
- Add regression tests for rebuild/integrity behavior and upsert recovery, plus a patch changeset for @runfusion/fusion
Previously this transition cleared only transient execution state
(status/error/worktree/blockedBy/workflowStepResults), but kept the
prior branch, baseBranch, baseCommitSha, summary, and recovery
counters. That meant retrying a reviewed task resumed on the old
branch with a stale summary instead of starting fresh. Now those
fields are also cleared on in-review → todo. Other reopen paths
(in-progress/done → todo/triage) keep their existing behavior.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
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>
Subtask, mission-interview, and milestone/slice-interview sessions could pin
their `generating` state forever when the underlying provider stream stalled
silently or a tool call hung. Wrap each `agent.session.prompt()` in a new
GenerationGuard helper (per-session AbortController + timer) so a stuck turn
becomes a bounded error users can retry. Adds matching `stop*Generation`
exports and threads abort through cleanup so dismissing a modal cancels the
in-flight call instead of leaking it.
Also closes the gh-cli tool hang vector: `runGhAsync` / `runGhJsonAsync` now
accept `{ signal, timeoutMs }` (default 30s). Github-touching extension tools
forward the AI tool's signal so an aborted agent kills the `gh` child instead
of orphaning it.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- Add effective node routing fields to task types/store, persist them in SQLite, and expand regression coverage for node override guard behavior
- Add dashboard and API support for manual memory dream processing/trigger actions plus expanded memory regression tests
- Improve task creation and task detail model/node UX, including quick chat default model selection and workflow/settings UI polish
- Apply mobile/dashboard UX fixes (form input zoom prevention, expand toggle styling, input layout tweaks) and update extension/docs/changelogs for the release
- Add a core node-override-guard module, export it from @fusion/core, and enforce conflicts in store updates
- Add API route and CLI extension safeguards so nodeId override updates are blocked when ownership would conflict
- Wire node override routing and validation through dashboard quick-create, list, modal, settings, and task form/detail surfaces
- Add focused unit and integration tests for core guard logic, workflow routes, and dashboard node override UX
- Define and export unavailable node policy types in core settings interfaces
- Add project-level unavailable node policy default and runtime validation helper
- Add core unit coverage for unavailable node policy parsing and acceptance cases
- Guard Paperclip mint requests to include companyId only when available for type-safe payloads
- Document unavailable node policy in the settings reference
- Improve automation startup diagnostics and route handling for manual execution steps
- Add support for full manual automation step execution in dashboard and engine flows
- Expand due-schedule coverage in automation store and dashboard route tests
- Add cron runner regression tests for edge cases and document the automation execution fix via changeset