779cd2e03094e8f163dbcb8b807e2bcd1107e2b2
3303 Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
fd43a57a41 |
FN-8179: align pi SDK versions with ModelRuntime API
Align workspace pi SDK dependencies with the ModelRuntime API required by the engine. - Pin pi AI and coding-agent packages to 0.80.10 across workspace consumers. - Keep session option typing compatible with the updated SDK contract. - Add a patch changeset and regenerate the dependency lockfile. Files changed: .changeset/fn-8179-pi-sdk-align.md | 7 + packages/cli/package.json | 4 +- packages/core/package.json | 2 +- packages/dashboard/package.json | 2 +- packages/engine/package.json | 4 +- packages/engine/src/pi.ts | 8 +- packages/pi-claude-cli/package.json | 8 +- .../src/thinking-config.ts | 9 +- pnpm-lock.yaml | 947 +++++++++++---------- pnpm-workspace.yaml | 5 + 10 files changed, 520 insertions(+), 476 deletions(-) Fusion-Task-Id: FN-8179 Fusion-Task-Lineage: aef45c2e-f353-4014-93de-44be91f43293 Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai> |
||
|
|
b687cc994e |
FN-8174: preserve live triage planning sessions
Keep active planning sessions protected from stale recovery while reclaiming genuinely hung triage work. - Retain stale processing entries that still have a live, non-aborted triage session. - Continue evicting no-session and stuck-aborted tasks so recovery can proceed. - Add triage and self-healing regression coverage, architecture guidance, and a patch changeset. Files changed: .changeset/fn-8174-planning-premature-todo.md | 7 ++ docs/architecture.md | 1 + packages/engine/src/__tests__/self-healing.test.ts | 102 +++++++++++++++++++++ packages/engine/src/__tests__/triage.test.ts | 37 +++++++- packages/engine/src/triage.ts | 48 +++++----- 5 files changed, 168 insertions(+), 27 deletions(-) Fusion-Task-Id: FN-8174 Fusion-Task-Lineage: f6811d72-95b4-4b5f-a71f-212f50e3ecdd Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai> |
||
|
|
5a60643c0a |
FN-8142: migrate auth storage and model runtime to pi SDK
Migrate Fusion's credential and model integrations to pi SDK 0.80.8+. - Replace legacy AuthStorage initialization with a locked Fusion credential store and ModelRuntime-backed registry. - Wire asynchronous model initialization and refresh through CLI, desktop, dashboard, executor, and provider paths. - Update provider, routing, and registry tests for the new SDK contracts. Files changed: packages/cli/src/commands/__tests__/daemon.test.ts | 2 +- .../cli/src/commands/__tests__/dashboard.test.ts | 9 +- .../cli/src/commands/__tests__/onboard.test.ts | 1 + packages/cli/src/commands/__tests__/serve.test.ts | 2 +- packages/cli/src/commands/daemon.ts | 19 +- packages/cli/src/commands/dashboard.ts | 20 +- packages/cli/src/commands/onboard.ts | 6 +- packages/cli/src/commands/serve.ts | 19 +- packages/cli/src/commands/startup-model-sync.ts | 4 +- packages/core/src/__tests__/openai-models.test.ts | 17 +- ...-model-routes-openai-codex-supplemental.test.ts | 17 +- ...register-model-routes-zai-real-registry.test.ts | 15 +- packages/dashboard/src/routes.ts | 12 +- .../dashboard/src/routes/register-model-routes.ts | 2 +- packages/desktop/src/local-runtime.ts | 2 +- packages/desktop/src/local-server.ts | 2 +- .../custom-providers-openai-completions.test.ts | 16 +- .../custom-providers-openai-responses.test.ts | 16 +- .../engine/src/__tests__/executor-test-helpers.ts | 2 +- .../src/__tests__/pi-create-fn-agent.test.ts | 8 +- .../engine/src/__tests__/pi-layers-wiring.test.ts | 2 +- packages/engine/src/__tests__/pi.test.ts | 47 ++--- .../src/__tests__/provider-registration.test.ts | 17 +- packages/engine/src/auth-storage.ts | 218 ++++++++++++++++++--- packages/engine/src/custom-provider-registry.ts | 14 +- packages/engine/src/executor.ts | 15 +- packages/engine/src/pi.ts | 50 +++-- packages/engine/src/provider-auth.ts | 58 +++--- packages/engine/src/provider-registration.ts | 16 +- 29 files changed, 421 insertions(+), 207 deletions(-) Fusion-Task-Id: FN-8142 Fusion-Task-Lineage: 8ae79064-7820-4976-9645-9431b5a3129e Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai> |
||
|
|
ca7a5a7106 |
FN-8144: remove workspace worktrees on archive
Archive workspace task worktrees synchronously and safely across archive entry points. - Add store-scoped workspace disposal planning, reservations, and quarantine handling. - Install baseline and executor disposers that remove per-repository worktrees and branches without shell interpolation. - Cover disposal-plan deduplication and document the archive cleanup behavior. Files changed: .../fn-8144-archive-removes-workspace-worktrees.md | 7 ++ AGENTS.md | 1 + docs/task-management.md | 4 + .../archive-removes-workspace-worktrees.test.ts | 59 +++++++++++ packages/core/src/archive-worktree-disposer.ts | 52 ++++++++++ packages/core/src/index.gate.ts | 8 ++ packages/core/src/index.ts | 8 ++ .../core/src/task-store/archive-lifecycle-2.ts | 29 ++++-- packages/core/src/task-store/archive-lifecycle.ts | 114 ++++++++++++++++++++- .../src/archive-worktree-disposer-install.ts | 27 ++++- packages/engine/src/executor.ts | 25 ++++- 11 files changed, 319 insertions(+), 15 deletions(-) Fusion-Task-Id: FN-8144 Fusion-Task-Lineage: 1c4b65f3-a1d2-4a5c-a4b6-c263f9e6f61d Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai> |
||
|
|
ee28f5e45e |
FN-8155: test triage retry title handling
Cover blank-title behavior across retryable triage failures. - Verify deterministic validation retries retain blank task titles - Verify transient failures retain blank task titles while retries remain Files changed: packages/engine/src/__tests__/triage.test.ts | 81 ++++++++++++++++++++++++++++ 1 file changed, 81 insertions(+) Fusion-Task-Id: FN-8155 Fusion-Task-Lineage: d8b4414d-b105-4251-bc42-d02a6d84bf3a Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai> |
||
|
|
c6be0b158b |
FN-8129: centralize database backup settings
Move database backup policy and scheduling to shared global configuration. - Split project memory backups from cluster-wide database backup settings. - Migrate legacy backup values and routines safely into central global storage. - Schedule and dispatch one shared PostgreSQL backup routine across project engines. Files changed: .changeset/fn-8129-backup-settings-scope-split.md | 7 + docs/dashboard-guide.md | 2 + docs/settings-reference.md | 10 +- packages/cli/src/commands/backup.ts | 3 +- .../__tests__/backup-settings-migration.test.ts | 50 ++++++ .../src/__tests__/backup-settings-scope.test.ts | 27 +++ packages/core/src/backup-settings-migration.ts | 188 +++++++++++++++++++++ packages/core/src/backup.ts | 77 +++++---- packages/core/src/global-routine-store.ts | 104 ++++++++++++ packages/core/src/index.gate.ts | 6 +- packages/core/src/index.ts | 6 +- .../core/src/postgres/migrations/0000_initial.sql | 19 +++ .../postgres/migrations/0015_global_routines.sql | 19 +++ packages/core/src/postgres/schema-applier.ts | 19 ++- packages/core/src/postgres/schema/central.ts | 21 ++- packages/core/src/postgres/startup-factory.ts | 11 ++ packages/core/src/settings-schema.ts | 14 +- packages/core/src/types.ts | 31 +++- .../dashboard/app/components/SettingsModal.tsx | 10 +- .../settings/__tests__/section-keys.test.ts | 1 + .../app/components/settings/save-split.ts | 2 + .../search/__tests__/settings-search-index.test.ts | 1 + .../settings/search/entries.ts | 2 + .../app/components/settings/section-keys.ts | 4 - .../settings/sections/BackupsSection.search.ts | 40 ----- .../settings/sections/BackupsSection.tsx | 112 +----------- .../sections/DatabaseBackupsSection.search.ts | 51 ++++++ .../settings/sections/DatabaseBackupsSection.tsx | 142 ++++++++++++++++ .../settings-default-descriptions.test.tsx | 1 + packages/dashboard/src/routes.ts | 12 +- .../src/routes/register-settings-memory-routes.ts | 41 ++--- .../engine/src/__tests__/routine-scheduler.test.ts | 55 +++++- packages/engine/src/cron-runner.ts | 4 +- packages/engine/src/routine-runner.ts | 67 +++++--- packages/engine/src/routine-scheduler.ts | 35 +++- 35 files changed, 929 insertions(+), 265 deletions(-) Fusion-Task-Id: FN-8129 Fusion-Task-Lineage: af17f39a-7f1c-40ff-8a4a-cd63895cd532 Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai> |
||
|
|
478f226a54 |
test: green full-suite CI after main drift (#2229)
## Summary Restores green **Full Suite (non-blocking)** runs on `main`. Recent main merges left i18n key parity, schema baseline bookkeeping (0011→0012), heartbeat tool inventory (FN-8058 `fn_task_logs_read`), and merger whitespace-classification mocks (execFile `git diff -p -w :2: :3:`) out of date, so all four test shards failed. ## Root causes observed on main - **Shard 4 / `@fusion/i18n`**: missing `skipConfirmationDialogs*` + `reviewBudgetExhausted` in non-en locales; orphan `awaitingApprovalPlanReviewReplanCap` - **Shard 3 / `@fusion/core`**: `SCHEMA_BASELINE_VERSION` advanced to `0012` while tests still equated it with `OWNER_PROJECT_ID_SPLIT_VERSION` (`0011`) and omitted `0012` from applied-migration lists - **Shards 1–2 / `@fusion/engine`**: tool count/snapshot drift for `fn_task_logs_read`; merger tests still mocked `git diff-tree` for trivial classification after the execFile `:2:`/`:3:` cutover; mock provider `updateTask` arity drift ## Changes - Locale catalogs: add missing keys, drop orphan key - Schema applier tests: immutable 0011 identity + baseline 0012 lists - Heartbeat + gating snapshots: include `fn_task_logs_read` - Merger unit mocks: recognize `git diff -p -w :2:path :3:path` - Mock provider: accept optional third `updateTask` arg ## Test plan - [x] `pnpm --filter @fusion/i18n exec vitest run` — 23/23 - [x] `pnpm --filter @fusion/core exec vitest run src/__tests__/postgres/schema-applier.test.ts` (immutable + automation upgrade) — pass - [x] `pnpm --filter @fusion/core exec vitest run` project-identity + satellite-fusiondir — pass - [x] Engine suites from failed CI shards (file-scoped, hermes/openclaw/paperclip/grok, reliability post-finalize/mission, heartbeat, gating, merger recovery/prompt, mock-provider, etc.) — pass - [ ] Full Suite workflow green on merge to main <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit - **New Features** - Improved project data isolation across backend operations. - Added safer optional toast handling when UI components render outside the full application shell. - Added support for reading task logs during agent heartbeat sessions. - **Bug Fixes** - Prevented runtime probes from hanging and avoided scanning large binary files. - Improved path handling for workspaces with missing descendants. - Corrected task retry state resets and GitHub import/issue-close behavior. - **Style** - Improved chat, terminal, and settings spacing. - Added clearer accessibility labeling for the auto-merge control. <!-- end of auto-generated comment: release notes by coderabbit.ai --> |
||
|
|
06a5fd813d |
refactor: package code organization waves 6–7 (#2166)
## Summary Waves 6–7 of package code organization (plan: `docs/plans/2026-07-14-001-refactor-package-code-organization-plan.md`), after #2148. ### Wave 6 | New module | Parent | |---|---| | `merger-autostash-labels.ts` | `merger.ts` | | `types/agent-state.ts` | `types.ts` | | `app/api/tasks-lifecycle.ts` | `legacy.ts` | | `task-store/task-row-mappers.ts` | `remaining-ops-3.ts` (rename) | ### Wave 7 | New module | Parent | |---|---| | `self-healing-optional-step-revision.ts` | `self-healing.ts` | | `self-healing-path-utils.ts` | `self-healing.ts` | | `merger-git-parse` (+ `quoteArg`, `getBranchChangedFiles`) | `merger.ts` | | `app/api/settings.ts` | `legacy.ts` | Public import paths stay stable via re-exports. ## Test plan - [x] engine + dashboard typecheck (incl. app) - [x] eslint on touched modules - [x] merger-autostash / parse-porcelain / getBranchChanged / api-tasks - [ ] CI merge gate <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **New Features** * Added a dedicated task lifecycle API client (task promotion, merge, retry/reset/duplicate, pause/unpause, archive/unarchive, revert, plan approve/reject) plus branch-group and planner oversight actions. * Added a settings/config API service (effective task settings, update check/refresh/install). * Introduced standardized agent lifecycle states with identity/ephemeral detection helpers. * **Bug Fixes** * Improved autostash label compatibility and NUL-delimited changed-file detection for branch diffs. * **Refactor** * Modularized merger labeling/parsing, self-healing helpers, and lifecycle/type wiring while keeping behavior consistent. * **Tests** * Updated merger verification tests for `git diff -z` output handling. <!-- end of auto-generated comment: release notes by coderabbit.ai --> |
||
|
|
edc64138e0 |
feat(worktrees): task-pinned worktrees under worktreeNaming "task-id" (#2233)
## Summary Adds **task-pinned worktrees** for `worktreeNaming: "task-id"`. Under task-id naming, a task is pinned to exactly one derivable directory `<worktreesDir>/<lowercased-task-id>` (e.g. `.worktrees/fn-7996`) for its entire lifecycle — removing the ambiguity that let stale/foreign `task.worktree` pointers strand a task (the FN-7996 shape). `recycleWorktrees` stays fully functional and is **mutually exclusive** with task-id pinning: the two can't be enabled together. ## Behavior - **Pinned acquisition (`worktreeNaming: "task-id"`, recycling off):** `acquireTaskWorktree` runs **derive → validate → reuse-or-recreate** at the derived path — warm-reuse when the dir is a registered, usable worktree on the task's own branch; otherwise reclaim-in-place (`removeWorktree` + recreate at the SAME path, never a sibling name). A disagreeing `task.worktree` cache self-corrects and emits a new `worktree:pin-rederived` audit event, without consuming worktree-session retries. The recycle pool is never consulted in pinned mode. - **Mutual exclusivity:** enabling both `recycleWorktrees` and `worktreeNaming: "task-id"` is rejected at the settings-write boundary — HTTP 400 at `PUT /settings`, and an `Error` backstop in `store.updateSettings` covering the CLI and every other writer (`assertWorktreeNamingRecycleExclusive`). The runtime also gates pinned mode on `!recycleWorktrees`, so a legacy on-disk config carrying both degrades safely to recycling. - **Settings UI:** the Settings → Worktrees panel enforces the exclusivity bidirectionally — the *Recycle worktrees* toggle is disabled while naming is *Task ID*, and the naming select is disabled while recycling is on — so the conflicting state is unreachable, with help text explaining why. - **Byte-inert for the rest:** `random`/`task-title` naming and the recycle pool (incl. `merger.ts` release) are unchanged; worktrunk-managed layouts bypass pinning. ## Acceptance criteria (from the plan) 1. ✅ Pinned task dispatched N times only ever touches `<worktreesDir>/<task-id>` on its own branch 2. ✅ No code path can hand task A's dir to task B (pool bypassed; path derived from task id) 3. ✅ FN-7996 stale/foreign `task.worktree` self-corrects at next dispatch (`worktree:pin-rederived`) without consuming session retries 4. ✅ Non-pinned modes with `recycleWorktrees: true|false` are byte-identical (existing pool tests pass unchanged) 5. ✅ Stale same-name dir (crash leftover / archive→restore) reclaimed in place, never suffixed 6. ✅ Docs updated (settings-reference, architecture, `worktreeNaming` type doc); changeset (`minor`, `feature`); FNXC comments encode the invariant ## Files - `packages/engine/src/worktree-pinning.ts` — new pure helpers (`isTaskPinnedWorktreeNaming`, `pinnedWorktreePathForTask`) - `packages/engine/src/worktree-acquisition.ts` — pinned branch + branch-match reclaim-in-place - `packages/engine/src/run-audit.ts` — `worktree:pin-rederived` audit type - `packages/core/src/settings-validation.ts` (+ `index.ts`, `task-store/settings-ops.ts`) — mutual-exclusion validator + wiring - `packages/dashboard/src/routes/register-settings-memory-routes.ts` — 400 on conflict - `packages/dashboard/app/components/settings/sections/WorktreesSection.tsx` (+ `packages/i18n/locales/en/app.json`) — bidirectional UI exclusivity - `packages/core/src/types.ts`, `docs/*`, `.changeset/*` ## Verification - New tests: engine `worktree-pinning` (5) + `worktree-acquisition-pinned` (7); core `worktree-naming-recycle-exclusive` (2); dashboard settings-route 400 (3) + WorktreesSection UI exclusivity (3) - Regression sweep green: 194 engine worktree/acquisition/pool/executor/merger-release tests, core settings tests, dashboard i18n/settings-section tests - `tsc --noEmit` clean for `@fusion/core` and `@fusion/engine`; changed source files clean; eslint clean - `pnpm verify:fast` PASS (build + scoped typecheck + boot smoke) 🤖 Generated with [Claude Code](https://claude.com/claude-code) <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **New Features** * Added Task ID worktree naming, providing each task with a stable, deterministic worktree directory. * Automatically reuses valid pinned worktrees and recreates stale or conflicting ones at the same path. * Added clear settings controls and validation for incompatible Task ID naming and worktree recycling options. * **Documentation** * Updated worktree architecture, settings reference, and in-app guidance to explain pinned worktrees and configuration constraints. * **Bug Fixes** * Improved recovery from stale or incorrect worktree assignments without consuming session retries. <!-- end of auto-generated comment: release notes by coderabbit.ai --> --------- Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com> |
||
|
|
f57dfc03b6 |
FN-8105: remove archived task worktrees safely
Archive task worktrees through a store-scoped, race-safe disposal lifecycle. - Reserve pinned worktree paths during archive cleanup and successor creation. - Reconcile quarantined removals before reusing a pinned path. - Gate PostgreSQL archival before destructive worktree disposal and wire CLI cleanup. Files changed: .changeset/fn-8105-archive-removes-worktree.md | 7 + docs/task-management.md | 4 + .../extension-experiment-finalize.test.ts | 1 + .../src/__tests__/extension-fn-secret-get.test.ts | 1 + .../extension-gitlab-tracking.test.ts | 1 + .../cli/src/__tests__/extension-web-fetch.test.ts | 1 + .../task-command-github-import-tracking.test.ts | 1 + packages/cli/src/commands/__tests__/task.test.ts | 1 + packages/cli/src/commands/task.ts | 8 +- packages/cli/src/extension.ts | 4 + .../__tests__/worktree-path-reservation.test.ts | 58 ++++++++ packages/core/src/archive-worktree-disposer.ts | 21 +++ packages/core/src/index.gate.ts | 13 ++ packages/core/src/index.ts | 13 ++ .../core/src/task-store/archive-lifecycle-2.ts | 8 ++ packages/core/src/task-store/archive-lifecycle.ts | 37 +++++ packages/core/src/worktree-path-reservation.ts | 149 +++++++++++++++++++++ .../src/archive-worktree-disposer-install.ts | 18 +++ packages/engine/src/executor.ts | 16 +++ packages/engine/src/index.ts | 2 + packages/engine/src/runtimes/in-process-runtime.ts | 1 + packages/engine/src/worktree-acquisition.ts | 27 +++- 22 files changed, 388 insertions(+), 4 deletions(-) Fusion-Task-Id: FN-8105 Fusion-Task-Lineage: cabb8f52-093f-4986-bfda-2c7601a72579 Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai> |
||
|
|
4e4b6be1b4 |
Plan-approval mailbox notification + triage Plan Review convergence (#2237)
## Summary Two related changes around the plan-approval flow, plus a fix for triage's plan-review loop that was stranding tasks at the replan cap. ### 1. Post a mailbox message when a plan needs approval (`feat`) The ntfy push on `awaiting-approval` already existed end-to-end. This adds the missing **durable, in-dashboard record**: a `system`-typed mailbox message linking to the task, written whenever a task enters `awaiting-approval`. - Fires **before** the notifications-enabled gate, so a dashboard-only operator (no ntfy/webhook configured) still gets the record — the whole point of the mailbox channel. - `system` type avoids re-triggering the `message:agent-to-user` ntfy pipeline (no double-notify); idempotent via `sendMessageOnce` (key `plan-approval:<taskId>`). ### 2. Help triage Plan Review converge before the replan cap (`fix`) Investigation of three tasks that burned all 8 plan-review replans without converging (**FN-7996, FN-8105, FN-8108**) found the reviewer surfaced a *new, deeper* issue each cycle instead of confirming its prior ones were fixed (goalpost movement), and reviewed specs at implementation altitude. This addresses the root causes: - **Feed the spec reviewer its own prior REVISE feedback + the 1-based replan attempt** so it verifies prior issues rather than moving goalposts. Gated to `reviewType === "spec"` and `attempt > 1` — **code review and normal plan review are byte-for-byte unaffected** (double-verified). - **Reviewer prompt:** converge-on-re-review rule (don't REVISE for your own earlier miss), severity ratchet (critical-only at attempt ≥ 3), and a **Spec Altitude** guard so exact SQL/lock/CAS protocol design is deferred to code review. - **Planner prompt:** front-load exhaustive surface enumeration before writing File Scope, and a storage-architecture ground-truth note (Postgres-only store, composite PK `(project_id, id)`, `schema-applier` migrations) to stop the repeated stale-fact REVISE rounds. ## Testing - `@fusion/core` + `@fusion/engine` typecheck clean. - Added coverage: reviewer spec-convergence wiring (attempt gating + code/plan exclusion + severity ratchet), triage prior-feedback derivation (incl. empty-output→notes fallback), mailbox decoupling (fires when push disabled) + rejection safety, and assertions for all new prompt sections. - Affected suites green: notification-service, reviewer, triage-plan-review-replan-cap, triage-replan-feedback, agent-prompts. - `pnpm check:changesets` passes (2 changesets: `@runfusion/fusion` minor + patch). ## Review Ran a 6-persona `ce-code-review` (correctness + adversarial on Opus; maintainability, testing, project-standards, api-contract). Guards verified unbreakable; no P0/P1 correctness or security issues. Applied the resulting fixes: decoupled the mailbox write from the push gate (P2), `??`→`||` in the feedback derivation (P3), de-duplicated the `specConvergence` ternary (P3), and closed the test-coverage gap the review flagged. 🤖 Generated with [Claude Code](https://claude.com/claude-code) <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit - **New Features** - Plans transitioning to **awaiting approval** now write an idempotent, task-linked **dashboard mailbox** message (approval reason + direct task link), even if push/notifications are disabled. - **Bug Fixes** - Plan Review **replan** behavior now better **converges** on prior REVISE feedback (including notes fallback) and stops looping at the replan cap. - At later attempts (attempt 3+), **REVISE** is applied to **critical** issues while lower-severity items shift to suggestions. - **Tests** - Added/expanded coverage for mailbox messaging and spec-convergence prompt wiring. <!-- end of auto-generated comment: release notes by coderabbit.ai --> --------- Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com> |
||
|
|
d870878a23 |
FN-7998: add executor alternate model escalation
Add opt-in executor escalation after same-model tool-failure retries are exhausted. - Persist escalation settings and one-shot task state across SQLite and PostgreSQL stores. - Retry once on a configured alternate model or scheduler node and audit escalation outcomes. - Expose escalation controls, documentation, translations, migration, and regression coverage. Files changed: .changeset/fn-7998-executor-escalation.md | 7 ++ AGENTS.md | 1 + docs/settings-reference.md | 13 ++- .../core/src/__tests__/settings-defaults.test.ts | 23 ++++- packages/core/src/in-review-stall.ts | 29 ++++++ packages/core/src/index.gate.ts | 3 +- packages/core/src/index.ts | 3 +- packages/core/src/manual-retry-reset.ts | 1 + .../0014_executor_escalation_attempt.sql | 2 + packages/core/src/postgres/schema-applier.ts | 17 ++++ packages/core/src/postgres/schema/project.ts | 1 + packages/core/src/settings-schema.ts | 4 + packages/core/src/store.ts | 2 +- packages/core/src/task-store/persistence.ts | 2 + packages/core/src/task-store/remaining-ops-2.ts | 2 +- packages/core/src/task-store/remaining-ops-3.ts | 2 +- packages/core/src/task-store/remaining-ops-6.ts | 2 +- packages/core/src/task-store/serialization.ts | 1 + packages/core/src/task-store/task-update.ts | 2 + packages/core/src/types.ts | 13 +++ .../dashboard/app/components/SettingsModal.tsx | 12 +++ .../app/components/settings/section-keys.ts | 4 + .../settings/sections/SchedulingSection.search.ts | 36 +++++++ .../settings/sections/SchedulingSection.tsx | 6 ++ .../settings-default-descriptions.test.tsx | 4 + .../__tests__/executor-tool-failure-retry.test.ts | 91 +++++++++++++++++- packages/engine/src/executor.ts | 104 +++++++++++++++++++-- packages/i18n/locales/en/app.json | 8 ++ 28 files changed, 376 insertions(+), 19 deletions(-) Fusion-Task-Id: FN-7998 Fusion-Task-Lineage: bbce767d-c61a-4667-be62-abc0cc54d8be Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai> |
||
|
|
2c3a777bca |
FN-8132: recover bare worktree branch collisions
Recover safe worktree creation when a dangling task branch already exists. - Classify bare branch collisions and preserve foreign or mixed commit history. - Reuse task-owned branches or recreate merged branches from the pinned start point. - Audit recovery outcomes and cover native, fallback, and workspace acquisition paths. Files changed: .../fn-8132-worktree-branch-collision-recovery.md | 7 ++ docs/architecture.md | 1 + .../__tests__/worktree-acquisition-backend.test.ts | 69 +++++++++++ .../worktree-acquisition-workspace.test.ts | 21 ++++ .../worktree-backend-branch-collision.test.ts | 132 +++++++++++++++++++++ packages/engine/src/branch-conflicts.ts | 121 +++++++++++++++++++ packages/engine/src/run-audit.ts | 1 + packages/engine/src/worktree-acquisition.ts | 3 +- packages/engine/src/worktree-backend.ts | 94 +++++++++++++++- 9 files changed, 447 insertions(+), 2 deletions(-) Fusion-Task-Id: FN-8132 Fusion-Task-Lineage: f09d140f-4fcd-48a6-99b1-a351630f37bd Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai> |
||
|
|
60b6e3e048 |
FN-7996: add configurable executor tool-failure retries
Add bounded, durable same-model retry handling for qualifying consecutive executor tool errors. - Persist retry claims, cursors, and audit markers with PostgreSQL migrations. - Expose project retry count, backoff, and failure threshold settings in the dashboard. - Cover retry, exhaustion, reset, and stale-run safety behavior with tests. Files changed: .changeset/fn-7996-executor-tool-failure-retry.md | 7 + AGENTS.md | 1 + docs/architecture.md | 1 + docs/settings-reference.md | 10 ++ .../executor-tool-failure-retry-claim.test.ts | 17 +++ .../core/src/__tests__/manual-retry-reset.test.ts | 3 + .../core/src/__tests__/settings-defaults.test.ts | 15 +- packages/core/src/in-review-stall.ts | 20 +++ packages/core/src/index.gate.ts | 6 + packages/core/src/index.ts | 6 + packages/core/src/manual-retry-reset.ts | 3 + .../0013_executor_tool_failure_retry.sql | 4 + packages/core/src/postgres/schema-applier.ts | 17 +++ packages/core/src/postgres/schema/project.ts | 3 + packages/core/src/settings-schema.ts | 3 + packages/core/src/store.ts | 10 +- packages/core/src/task-store/persistence.ts | 7 + packages/core/src/task-store/remaining-ops-2.ts | 2 +- packages/core/src/task-store/remaining-ops-3.ts | 2 +- packages/core/src/task-store/remaining-ops-6.ts | 65 ++++++++- packages/core/src/task-store/serialization.ts | 3 + packages/core/src/task-store/task-update.ts | 6 + packages/core/src/types.ts | 16 +++ .../dashboard/app/components/SettingsModal.tsx | 15 ++ .../app/components/settings/section-keys.ts | 3 + .../settings/sections/SchedulingSection.search.ts | 27 ++++ .../settings/sections/SchedulingSection.tsx | 4 + .../settings-default-descriptions.test.tsx | 3 + .../__tests__/executor-tool-failure-retry.test.ts | 160 +++++++++++++++++++++ packages/engine/src/executor.ts | 87 ++++++++++- packages/i18n/locales/en/app.json | 6 + 31 files changed, 523 insertions(+), 9 deletions(-) Fusion-Task-Id: FN-7996 Fusion-Task-Lineage: d1682ef8-534c-410e-b74c-1f2cf176eac2 Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai> |
||
|
|
bd4857a44e |
FN-8136: update cron-runner backup error expectations
Align cron-runner backup failure assertions with the current PostgreSQL project-state error wording. - Update legacy command backup failure expectation - Update command-step backup failure expectation Files changed: packages/engine/src/__tests__/cron-runner.test.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) Fusion-Task-Id: FN-8136 Fusion-Task-Lineage: 8f182491-1ded-43bb-b4ca-02485dce656a Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai> |
||
|
|
fdea8a4294 |
FN-8118: document continuation rescue test coverage
Document why the verified post-done continuation rescue suite remains unquarantined. - Record the serialized in-memory reliability coverage and its exclusion rationale. - Preserve the engine-default reliability partition exclusion. Files changed: packages/engine/vitest.config.ts | 2 ++ 1 file changed, 2 insertions(+) Fusion-Task-Id: FN-8118 Fusion-Task-Lineage: 0ac7f24a-888c-470c-b175-98b4e0411061 Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai> |
||
|
|
2142841f0c |
FN-8111: restore reliability test coverage
Restore PostgreSQL-compatible reliability coverage and prevent completed tasks from wedging on stale continuation recovery. - Update reliability fixtures and audit assertions for PostgreSQL-backed stores - Prioritize completed-task handling before stale assistant-continuation retries - Unquarantine the restored meta-archive and continuation reliability suites Files changed: .../explicit-duplicate-marker-sweep.test.ts | 4 ++++ .../meta-archive-guard-composition.test.ts | 26 +++++++++++++++++----- .../post-done-continuation-no-wedge.test.ts | 3 ++- packages/engine/src/executor.ts | 7 ++++++ packages/engine/vitest.config.ts | 4 ++-- scripts/lib/test-quarantine.json | 10 --------- 6 files changed, 36 insertions(+), 18 deletions(-) Fusion-Task-Id: FN-8111 Fusion-Task-Lineage: 8b30b5cb-c160-44e1-8e8c-dd58f4877edc Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai> |
||
|
|
4a797d3804 |
FN-8117: restore explicit duplicate marker sweep coverage
Configure duplicate-marker PG fixtures with canonical FN task IDs so the sweep coverage exercises real deletion paths. - Set taskPrefix to FN for duplicate-marker reliability fixtures. - Remove the corrected test from the PG quarantine ledger and Vitest exclusions. - Document why valid marker IDs are required for this coverage. Files changed: .../explicit-duplicate-marker-sweep.test.ts | 20 +++++++++++++------- packages/engine/vitest.config.ts | 4 +++- scripts/lib/test-quarantine.json | 5 ----- 3 files changed, 16 insertions(+), 13 deletions(-) Fusion-Task-Id: FN-8117 Fusion-Task-Lineage: 3b09cbbe-924c-4e3c-849b-cf7643b0ac0e Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai> |
||
|
|
0b488523c2 |
FN-8104: retire legacy SQLite database fallbacks
Retire legacy SQLite database calls from PostgreSQL-only startup and self-healing paths. - Route plugin schema initialization exclusively through the PostgreSQL executor. - Delegate soft-delete column repair to the PostgreSQL reconciliation seam. - Remove temporary getDatabase allowlist entries and add no-SQLite regression coverage. Files changed: .../postgres/store-safe-defaults.pg.test.ts | 14 +++++- packages/core/src/store.ts | 24 ++++------ .../engine/src/__tests__/plugin-runner.test.ts | 6 --- .../self-healing-fake-overlap-seam.test.ts | 44 +++++++++++++++++++ packages/engine/src/self-healing.ts | 51 +++++----------------- scripts/lib/getdatabase-allowlist.json | 17 +------- 6 files changed, 77 insertions(+), 79 deletions(-) Fusion-Task-Id: FN-8104 Fusion-Task-Lineage: 88dee027-51c3-4c68-94dc-88191fe20330 Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai> |
||
|
|
aa07a78f18 |
fix: recover graph-node missing-worktree failures instead of terminal-parking (FN-7996) (#2231)
## Why FN-7996 sat in a dispatch→park loop **all day** (06:06→16:35): its `worktree` metadata pointed at recycled pool worktrees (`coral-badger`, `grand-ridge` — the latter actually belonged to FN-8069), Plan Review refused to start in the missing directory, and the task terminal-parked `failed` every cycle while the planner overseer blindly retried. Root-cause chain: 1. **`graphFailureValue()` couldn't read optional-group results.** `runOptionalGroup` publishes context under the group id (`node:plan-review:value`) and the unqualified template id, but the failed node is recorded as the materialized `plan-review::plan-review-step` — the lookup only understood `#` foreach ids. FN-7977's provider-failure hold *did* classify this failure, but its hold value was invisible to routing. 2. **No graph-failure router handled the `assertValidWorktreeSession` refusal**, so it fell to the terminal sink, which parked the task and *overwrote* `task.error` with a generic message — erasing the signature the missing-worktree self-healing sweep (in-review-only anyway) classifies on. 3. **Plan Review didn't need the worktree at all** — its spec is store-injected (FN-7561) — yet it launched its reviewer in whatever stale `task.worktree` said. ## What - `handleGraphFailure` routes unusable-worktree node failures (any node, any error key, `::`/`#` materialized ids) into the existing bounded worktree-session recovery: clear stale worktree/branch/session metadata, requeue to todo, budgeted by `worktreeSessionRetryCount`. An exhausted budget still falls through to the visible terminal park for human inspection. - `graphFailureValue` resolves `group::template` ids (group value first — it carries post-classification routing intent — then the unqualified template value). Foreach `#` behavior unchanged. - Plan Review falls back to the repo root when its recorded worktree is missing on disk; other read-only gates intentionally keep failing fast into the new recovery (silently retargeting them to root would review the wrong tree). - `recoverMissingWorktreeSessionStartFailure` returns its outcome so the graph router can distinguish requeue from escalate-exhausted; existing truthy callers unchanged. ## Symptom Verification - **Original symptom:** graph-node session-start refusal → `Workflow graph terminated with failure at node 'plan-review::plan-review-step'`, task parked failed with stale metadata intact, no recovery. - **Reproduction:** `graph-node-missing-worktree-recovery.test.ts` drives `handleGraphFailure` with the exact FN-7996 result shape (optional-group materialized id + `Refusing to start coding agent in missing worktree` node error). - **Assertion it is gone:** the task is requeued to `todo` with `worktree`/`branch`/`sessionFile` cleared and retry budget incremented — and is *not* marked `failed`; budget exhaustion still parks visibly. ## Surface Enumeration - Optional-group template nodes (Plan Review — the repro), write-capable review gates, and any custom graph node: covered by the `handleGraphFailure` router (scans exact/materialized/unqualified `:error` keys). - Execute-seam session start: already covered by the pre-existing recovery (unchanged, still passes). - In-review / merge-active columns: already covered by self-healing sweeps (unchanged). - Paused / user-paused / deleted / done tasks: explicitly left to their owning machinery (guard tests). - Budget exhaustion: falls through to the visible terminal park (test). ## Testing - `pnpm --filter @fusion/engine exec vitest run src/__tests__/reliability-interactions/graph-node-missing-worktree-recovery.test.ts` — 13 passed - Adjacent suites (`worktree-incomplete-session-start`, `executor-graph-requeue-gate`, `workflow-graph-optional-group`, `executor-paused-abort-todo-benign`) — 78 passed - `tsc --noEmit` on `@fusion/engine` — clean 🤖 Generated with [Claude Code](https://claude.com/claude-code) <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **Bug Fixes** * Improved recovery when workflow tasks encounter missing or recycled worktrees. * Automatically retries affected tasks with stale worktree details cleared, up to the configured retry limit. * Escalates tasks after recovery attempts are exhausted. * Improved failure routing for optional workflow groups and template instances. * Plan Review now falls back to the repository root when its recorded worktree is unavailable. * **Tests** * Added regression coverage for recovery, routing, retry limits, and repository-root fallback behavior. <!-- end of auto-generated comment: release notes by coderabbit.ai --> --------- Co-authored-by: Claude Fable 5 <noreply@anthropic.com> |
||
|
|
1a337df5e9 |
FN-8098: add executor model fallback
Add workflow-specific executor fallback configuration and bounded recovery. - Add executor fallback provider, model, and thinking settings across core schemas and settings UI. - Route executor, heartbeat, child, and workflow-step sessions through the executor fallback resolver. - Retry the primary model after fallback failure before reporting terminal exhaustion. Files changed: .changeset/fn-8098-model-fallback.md | 7 +++ docs/settings-reference.md | 7 ++- .../core/src/__tests__/model-resolution.test.ts | 13 ++++ .../core/src/__tests__/settings-parity.test.ts | 5 ++ packages/core/src/builtin-workflow-settings.ts | 24 +++++++ packages/core/src/index.gate.ts | 1 + packages/core/src/index.ts | 1 + packages/core/src/model-resolution.ts | 21 +++++++ packages/core/src/settings-schema.ts | 3 + packages/core/src/types.ts | 11 ++++ .../app/components/WorkflowSettingsPanel.tsx | 8 +++ .../settings/sections/ProjectModelsSection.tsx | 10 ++- packages/engine/src/__tests__/pi.test.ts | 14 ++++- packages/engine/src/agent-session-helpers.ts | 7 ++- packages/engine/src/executor.ts | 48 +++++++------- packages/engine/src/pi.ts | 73 ++++++++-------------- packages/engine/src/step-session-executor.ts | 8 ++- 17 files changed, 180 insertions(+), 81 deletions(-) Fusion-Task-Id: FN-8098 Fusion-Task-Lineage: 61b3103b-357b-431a-8d58-411e7806b87b Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai> |
||
|
|
14953f39e2 |
FN-8075: restore PostgreSQL self-healing test coverage
Restore self-healing coverage for PostgreSQL-backed maintenance. - Update self-healing mocks and assertions for asynchronous audit APIs and PostgreSQL WAL behavior - Align git command expectations and transient recovery budget coverage with current implementation - Remove the repaired self-healing suite from the quarantine ledger and gate exclusion Files changed: packages/engine/src/__tests__/self-healing.test.ts | 70 ++++++++++++---------- packages/engine/vitest.config.ts | 1 - scripts/lib/test-quarantine.json | 5 -- 3 files changed, 39 insertions(+), 37 deletions(-) Fusion-Task-Id: FN-8075 Fusion-Task-Lineage: 7dfce9b0-9d10-4d70-8e92-8100f6595fce Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai> |
||
|
|
40687cb0be |
FN-8050: cover cached dependency reconciliation seeding
Ensure raw dependency-reconciliation fixtures invalidate warmed task caches. - Warm slim task-list caches before corrupt-row seeding in cycle reconciliation tests. - Document the PostgreSQL-only fixture seam and cache invalidation rationale. Files changed: .../engine/src/__tests__/reliability-interactions/_helpers.ts | 11 ++++++----- .../dependency-cycle-reconcile.test.ts | 7 +++++++ 2 files changed, 13 insertions(+), 5 deletions(-) Fusion-Task-Id: FN-8050 Fusion-Task-Lineage: 5e708d58-faed-4271-a075-24c36eaa5878 Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai> |
||
|
|
50ed689379 |
FN-8044: restore dependency reconcile reliability tests
Restore dependency reconciliation coverage using a PostgreSQL raw-seeding seam. - Add a cache-invalidating raw task-column seeding helper for corrupt fixture states. - Update dependency-cycle and self-defeating reconciliation tests to use the PG seam. - Remove restored suites from the reliability quarantine configuration and ledger. Files changed: .../__tests__/reliability-interactions/_helpers.ts | 29 ++++++++ .../dependency-cycle-reconcile.test.ts | 86 +++++++++++----------- .../self-defeating-dep-reconcile.test.ts | 7 +- packages/engine/vitest.config.ts | 7 +- scripts/lib/test-quarantine.json | 10 --- 5 files changed, 82 insertions(+), 57 deletions(-) Fusion-Task-Id: FN-8044 Fusion-Task-Lineage: 213f606c-1951-4d25-93b4-2ceb97460ace Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai> |
||
|
|
969fce7aa1 |
FN-8047: migrate AgentStore multi-node tests to PostgreSQL
Migrate multi-node AgentStore coverage to shared PostgreSQL-backed fixtures. - Make concurrent central claim insertion resolve unique-key races as checkout conflicts. - Rework claim and owning-node handoff tests to use shared async PostgreSQL layers. - Restore PostgreSQL-compatible tests from the quarantine ledger. Files changed: packages/core/src/async-central-db.ts | 9 ++- .../cross-node-claim-mutex.integration.test.ts | 72 ++++++++++--------- .../distributed-claim-mutex.integration.test.ts | 27 +++---- .../owning-node-handoff.integration.test.ts | 41 +++++------ .../__tests__/reliability-interactions/_helpers.ts | 83 ++++++++++++++++++++-- .../multi-node-claim-mutex-interactions.test.ts | 28 +++----- .../owning-node-unavailable-interactions.test.ts | 36 +++++----- packages/engine/vitest.config.ts | 8 +-- scripts/lib/test-quarantine.json | 25 ------- 9 files changed, 180 insertions(+), 149 deletions(-) Fusion-Task-Id: FN-8047 Fusion-Task-Lineage: 3b7ee21e-0190-4364-a0cb-88aac5e2e1a3 Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai> |
||
|
|
19ab7a9e93 |
FN-8068: add input-language task definitions
Add an opt-in setting that localizes planner-authored task-definition prose. - Detect confident Spanish, French, Korean, and Chinese input before instructing triage localization. - Expose and persist the project-level task-definition language toggle with translations and search support. - Preserve English structural markers, code, and unsupported or uncertain input for deterministic parsing. Files changed: .../fn-8068-task-definition-input-language.md | 7 +++ docs/settings-reference.md | 1 + packages/core/src/settings-schema.ts | 7 +++ packages/core/src/types.ts | 10 ++++ .../app/__tests__/settings-sections.test.tsx | 18 ++++++ .../dashboard/app/components/SettingsModal.tsx | 4 ++ .../__tests__/SettingsModal.mobileClose.test.tsx | 12 ++++ .../__tests__/SettingsModal.models-auth.test.tsx | 11 ++++ .../app/components/settings/section-keys.ts | 1 + .../sections/ProjectModelsSection.search.ts | 10 ++++ .../settings/sections/ProjectModelsSection.tsx | 17 ++++++ .../settings-default-descriptions.test.tsx | 1 + packages/engine/src/__tests__/triage.test.ts | 64 ++++++++++++++++++++++ packages/engine/src/triage.ts | 29 +++++++++- packages/i18n/locales/en/app.json | 2 + packages/i18n/locales/es/app.json | 2 + packages/i18n/locales/fr/app.json | 2 + packages/i18n/locales/ko/app.json | 2 + packages/i18n/locales/zh-CN/app.json | 2 + packages/i18n/locales/zh-TW/app.json | 2 + packages/i18n/src/resources.d.ts | 2 + 21 files changed, 205 insertions(+), 1 deletion(-) Fusion-Task-Id: FN-8068 Fusion-Task-Lineage: fe0102f5-68cb-443c-8512-2ffa5fe0e2f8 Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai> |
||
|
|
5b4ec4c08f |
FN-8042: add merger fallback model lane
Add configurable project fallback models for AI merger retries. - Resolve complete project merger fallback pairs before the shared global fallback. - Expose merger fallback model and thinking controls in Project Models with reset-aware persistence. - Apply the fallback lane across merger, review, PR-response, and recovery sessions with tests and documentation. Files changed: .changeset/fn-8042-merger-fallback-model.md | 7 +++ docs/settings-reference.md | 4 +- .../core/src/__tests__/model-resolution.test.ts | 24 ++++++++++ .../core/src/__tests__/settings-parity.test.ts | 3 ++ packages/core/src/index.gate.ts | 1 + packages/core/src/index.ts | 1 + packages/core/src/model-resolution.ts | 22 +++++++++ packages/core/src/settings-schema.ts | 4 ++ packages/core/src/types.ts | 13 ++++++ .../app/__tests__/settings-save-split.test.ts | 41 +++++++++++++++++ .../app/__tests__/settings-sections.test.tsx | 36 +++++++++++++++ .../app/components/settings/save-split.ts | 7 +-- .../settings/sections/ProjectModelsSection.tsx | 53 +++++++++++++++++++++- .../settings-default-descriptions.test.tsx | 3 ++ .../src/__tests__/agent-session-helpers.test.ts | 1 + .../__tests__/mcp-pr-response-forwarding.test.ts | 1 + packages/engine/src/agent-session-helpers.ts | 10 +++- packages/engine/src/merger-ai.ts | 17 +++++-- packages/engine/src/merger.ts | 43 +++++++++++++----- packages/engine/src/pr-response-run-ops.ts | 11 +++-- 20 files changed, 276 insertions(+), 26 deletions(-) Fusion-Task-Id: FN-8042 Fusion-Task-Lineage: 31762a72-461e-438c-a12d-2816580283fd Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai> |
||
|
|
3f133e0b13 |
FN-8058: add task agent log reader
Expose paginated, filterable persisted agent logs to task-scoped and chat agent sessions. - Add the read-only fn_task_logs_read tool across engine, dashboard chat/planning, heartbeat, step, and CLI extension surfaces. - Filter agent-log entries before pagination, report matching totals, and render complete persisted rows for diagnosis. - Document the tool, add release metadata, regression coverage, and complete affected engine mocks. Files changed: .changeset/fn-8058-task-logs-read.md | 7 ++ docs/agents.md | 4 +- packages/cli/skill/fusion/SKILL.md | 2 +- .../cli/skill/fusion/references/extension-tools.md | 11 +++ .../skill/fusion/references/fusion-capabilities.md | 1 + .../extension-experiment-finalize.test.ts | 2 + .../src/__tests__/extension-fn-secret-get.test.ts | 2 + .../__tests__/extension-gitlab-tracking.test.ts | 2 + .../src/__tests__/extension-integration.test.ts | 1 + .../cli/src/__tests__/extension-web-fetch.test.ts | 2 + packages/cli/src/__tests__/extension.test.ts | 1 + packages/cli/src/extension.ts | 34 ++++++++ .../src/__tests__/agent-logs-backend-mode.test.ts | 28 +++++- packages/core/src/store.ts | 11 ++- packages/core/src/task-store/remaining-ops-7.ts | 16 +++- packages/core/src/types.ts | 1 + packages/dashboard/src/__tests__/chat.test.ts | 1 + .../planning-answered-question-reemit.test.ts | 1 + .../planning-generation-cancellation.test.ts | 1 + packages/dashboard/src/chat.ts | 5 ++ packages/dashboard/src/planning.ts | 3 + .../__tests__/agent-task-logs-read-tools.test.ts | 72 ++++++++++++++++ packages/engine/src/agent-heartbeat.ts | 4 +- packages/engine/src/agent-tools.ts | 99 +++++++++++++++++++++- packages/engine/src/executor.ts | 6 ++ packages/engine/src/gating-classifications.ts | 2 + packages/engine/src/index.ts | 6 ++ packages/engine/src/step-session-executor.ts | 6 +- 28 files changed, 316 insertions(+), 15 deletions(-) Fusion-Task-Id: FN-8058 Fusion-Task-Lineage: 74f198b2-f538-4b39-973f-431f22e68f29 Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai> |
||
|
|
c2475b012d |
FN-8064: add proactive task chat status updates
Task-detail chat now narrates engine progress and review outcomes in real time. - Emit bounded, redacted status rows for step lifecycle and review paths. - Present status entries with a distinct task-chat treatment. - Cover status narration and diagnostic sanitization with engine tests. Files changed: .changeset/fn-8064-proactive-chat.md | 7 + docs/architecture.md | 1 + packages/dashboard/app/components/TaskChatTab.css | 16 ++ packages/dashboard/app/components/TaskChatTab.tsx | 9 +- .../engine/src/__tests__/executor-prompt.test.ts | 28 +++- .../engine/src/__tests__/proactive-status.test.ts | 54 +++++++ packages/engine/src/executor.ts | 176 ++++++++++++++++----- packages/engine/src/proactive-status.ts | 117 ++++++++++++++ 8 files changed, 365 insertions(+), 43 deletions(-) Fusion-Task-Id: FN-8064 Fusion-Task-Lineage: c6d0a9b5-0946-4bf4-8338-e982e1cbfd53 Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai> |
||
|
|
274318aebf |
FN-8056: enforce task token budgets
Enforce configured task token budgets whenever session usage is persisted. - Apply soft alerts and hard pauses atomically from all executor persistence paths. - Exclude cache-read tokens from budget usage and dispatch budget notifications once. - Document budget semantics and add regression coverage. Files changed: .changeset/fn-8056-token-budget-enforcement.md | 7 ++ docs/settings-reference.md | 2 + packages/core/src/types.ts | 4 +- .../src/__tests__/session-token-usage.test.ts | 101 ++++++++++++++++++++- .../src/__tests__/token-budget-enforcer.test.ts | 81 ++++++++++------- packages/engine/src/executor.ts | 22 ++++- packages/engine/src/session-token-usage.ts | 8 +- packages/engine/src/token-budget-enforcer.ts | 98 +++++++++++++++++--- 8 files changed, 262 insertions(+), 61 deletions(-) Fusion-Task-Id: FN-8056 Fusion-Task-Lineage: 5f5ed522-f950-42ce-b4fd-e0b1d45b5815 Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai> |
||
|
|
e46ffebde1 |
FN-7985: surface review budget exhaustion and configure replan cap
Expose exhausted Plan Review replan budgets for operator approval and allow workflows to configure the cap. - Add validated numeric workflow setting support and a Plan Review replan-cap setting. - Route configured cap exhaustion with a distinct approval reason and preserve fallback behavior. - Display the budget-exhaustion state across task cards, lists, and details. - Add tests, localized copy, documentation, and a minor changeset. Files changed: .changeset/fn-7985-review-budget-approval.md | 7 ++++ docs/settings-reference.md | 9 +++-- docs/workflow-steps.md | 2 +- .../builtin-workflow-settings-triage.test.ts | 27 +++++++++++++-- packages/core/src/builtin-workflow-settings.ts | 17 ++++++++++ packages/core/src/index.gate.ts | 1 + packages/core/src/index.ts | 1 + packages/core/src/workflow-ir-types.ts | 4 +++ packages/core/src/workflow-ir.ts | 27 +++++++++++++++ packages/core/src/workflow-settings-resolver.ts | 1 + packages/core/src/workflow-settings.ts | 6 ++++ packages/dashboard/app/components/ListView.css | 19 +++++++++++ packages/dashboard/app/components/ListView.tsx | 22 +++++++++--- packages/dashboard/app/components/TaskCard.css | 18 ++++++++++ packages/dashboard/app/components/TaskCard.tsx | 6 ++-- .../dashboard/app/components/TaskDetailModal.tsx | 4 +-- .../app/components/__tests__/ListView.test.tsx | 30 +++++++++++++++++ .../app/components/__tests__/TaskCard.test.tsx | 17 ++++++++-- .../app/components/workflow-setting-display.ts | 11 ++++++ .../dashboard/app/utils/reviewBudgetApproval.ts | 11 ++++++ .../triage-plan-review-replan-cap.test.ts | 39 +++++++++++++++++++--- packages/engine/src/triage.ts | 21 ++++++++---- packages/i18n/locales/en/app.json | 2 +- packages/i18n/src/resources.d.ts | 19 ++++++++--- 24 files changed, 288 insertions(+), 33 deletions(-) Fusion-Task-Id: FN-7985 Fusion-Task-Lineage: 125f101c-caca-45c2-8b40-996b2a31c019 Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai> |
||
|
|
a31c370375 |
FN-8045: add transactional handoff failure-injection seam
Ensure PostgreSQL review handoffs roll back all dependent writes after an injected late failure. - Add a test-only failure injector after transactional handoff writes. - Include workflow work in same-column retry transactions. - Restore PG-backed handoff atomicity coverage and remove its quarantine. Files changed: packages/core/src/store.ts | 24 +++ packages/core/src/task-store/moves.ts | 21 ++- .../in-review-handoff-atomic.test.ts | 172 +++++++++++++-------- packages/engine/vitest.config.ts | 1 - scripts/lib/test-quarantine.json | 5 - 5 files changed, 151 insertions(+), 72 deletions(-) Fusion-Task-Id: FN-8045 Fusion-Task-Lineage: 517e3000-9b88-4b0d-9b25-1a585eb8f322 Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai> |
||
|
|
9a34862586 |
refactor: package code organization waves 3–5 (#2148)
## Summary Waves 3–5 of package code organization (plan: `docs/plans/2026-07-14-001-refactor-package-code-organization-plan.md`). Behavior-preserving peels after #2139 and #2143. ### Wave 3 — Merger + heartbeat recovery - **`merger-errors.ts`** — verification/abort error classes - **`merger-owned-landed.ts`** — ownership classification + `Fusion-Task-Id` trailer - **`merger-conflict-resolution.ts`** — conflict classify/auto-resolve - **`agent-heartbeat-error-recovery.ts`** — durable error-recovery budget helpers ### Wave 4 — Self-healing + dashboard API - **`self-healing-constants.ts`** — public timing/budget constants - **`self-healing-branch.ts`** — `isBranchAheadOfBase` - **`app/api/client.ts`** — `api` / `ApiRequestError` / `buildApiUrl` / `proxyApi` - **`app/api/health.ts`** — health, engine status, updates + `withProjectId` ### Wave 5 — Types tracking + merger parse + task CRUD - **`types/task-tracking.ts`** — PR/issue/GitHub/GitLab tracking contracts - **`merger-git-parse.ts`** — `parseFailingFilesFromOutput`, `parsePorcelainZ`, `parseShortstatSummary` - **`app/api/tasks.ts`** — task list/detail/create/update/move client surface - Line-count baselines ratcheted down for `merger.ts`, `types.ts`, `legacy.ts` Public import paths stay on parent modules / `legacy.ts` / package barrels via re-exports. ## Test plan - [x] core/engine/dashboard typecheck (including `tsconfig.app.json`) - [x] eslint on touched modules - [x] `parse-porcelain-z` + merger parseFailing/getBranchChanged tests - [x] dashboard `api-tasks` + legacy-prinfo/pr-types (69) - [ ] CI merge gate <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit - **New Features** - Added dashboard API support for task listing/detail, archiving, creation, review updates, duplicate detection, bulk model updates, moving tasks, and overlap repair. - Added health/engine status and refresh/start controls, plus update checking. - **Bug Fixes** - Improved dashboard API handling for non-JSON/HTML responses with clearer errors, better URL routing for remote nodes, and project-scoped queries. - Strengthened automated recovery for heartbeat error/model-unavailable scenarios and safer merge-conflict classification/auto-resolution. - **Tests** - Updated merge-conflict resolution and lifecycle test mocks to match the updated git command behavior. <!-- end of auto-generated comment: release notes by coderabbit.ai --> |
||
|
|
1d6a0449ce |
fix(engine): keep replan cards plannable when they carry prior steps
Tasks piled up in the Planning column and never moved. hasAdvancedPastPlanning counted steps.length > 0 as proof a card had advanced past planning, but a replan card legitimately retains the steps its previous planning pass materialized. The still-in-planning guard therefore failed for every card Plan Review sent back, so triage's specifyTask claim silently skipped its status:"planning" write and re-claimed the same cards every poll — never planning them, and starving healthy cards out of the maxTriageConcurrent slots they held. Steps are no longer advancement evidence while a card sits in a planner lane: the "triage" column, and the merged "todo" planner lane used by plan-in-place workflows when the card carries a planning status. Worktrees and execution/terminal columns remain durable advancement evidence, preserving FN-7977's protection against a recovery write clobbering a card that raced ahead into execution. The primary claim path now warns instead of returning silently; recovery-write skips stay silent by design. The silence is why this stalled the planner for hours undiagnosed. Regression coverage asserts the invariant across both planner surfaces rather than the reported repro alone: triage cards with and without an explicit needs-replan status, plan-in-place todo replans, every parked-for-planning status, and the advancement signals that must still fire. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
||
|
|
7b31d54b98 |
fix(FN-8004): make AI merge rejections actionable and stranded merges retryable (#2160)
Two follow-ups to FN-8004. Both were found by watching FN-8004's *own* merge livelock for 40 minutes — it turned out to be blocked by the very class of bug it was filed to fix. ## 1. AI merge rejections lost their reasons The reviewer prompt said **both** of these: > "**End with a single decision line**: `REVIEW_VERDICT: approve|reject`" > "**Then list each concrete reason as a bullet.**" Those are impossible to satisfy at once. Reviewers obeyed "End with" and wrote their reasoning *above* the verdict — but `extractRejectReasons` only scanned lines *after* it. So every such rejection collapsed to the placeholder `reviewer rejected the merge without a stated reason`, and that placeholder was then handed to the corrective re-merge pass **as its instruction**. The pass got no actionable feedback and just re-rolled the merge. The evidence, from FN-8004's own merge — the pattern repeated across *both* attempts: | | Attempt A | Attempt B | |---|---|---| | review pass 1 | rejected, no reason (03:46) | rejected, no reason (03:57) | | corrective pass | 1/3 | 1/3 | | review pass 2 | **approved** `a3a3cc6a8` (03:49) | approved | A reviewer that rejects and then approves identical content isn't objecting — the reason was being thrown away. Each wasted cycle cost ~7 minutes, stretching the merge past main's ~8-minute churn window so every attempt lost to a concurrent advance and rebuilt. **The livelock was caused by the lost-reason bug.** Fix: the parser recovers reasons from either side of the verdict (inline → after → before, nearest-first so the closing argument leads, capped at 8 so a long transcript can't flood the corrective prompt), skipping severity/verdict/markdown scaffolding. The prompt ordering is now unambiguous — reasons first, verdict last, nothing after it. ## 2. An orphaned merge-active stamp was un-retryable by hand The Retry gate refused **every** merge-active status (`Task is not in a retryable state (current status: landing)`), while self-healing cleared stale stamps automatically minutes later. So a merger killed mid-flight — crash, engine restart, operator SIGTERM — blocked the operator's own escape hatch at exactly the moment they'd reach for it. FN-8004 hit this: a killed merge left `landing` stamped and Retry 400'd for the full sweep delay. `isStaleMergeActiveStatus` now lives in the leaf `merge-active-status.ts`, shared by `recoverStaleMergingStatus` and the Retry gate — so **the manual path can never be stricter than the automatic one**. This is the same one-concept-two-definitions bug as FN-8004's transient classifier, which is why it's worth fixing structurally rather than adding another special case. A live merge stays protected by two independent signals: it holds the in-process lease **and** refreshes `updatedAt` each phase. Staleness fails closed on an unparseable timestamp. One subtlety worth reviewing: the bypass feeds `isInReviewRetry` rather than only the gate. A bare gate bypass would fall through to the generic branch and move fully-executed work to `todo`, **re-running finished work** — a bug this fix could easily have introduced. ## Verification - Gate green (294 + 122 + 63) · lint clean · engine + dashboard typecheck clean · `verify:fast` PASS - 70 merger-suite tests green; all 7 pre-existing verdict-parser tests still pass (backward compatible — none of them covered the verdict-last layout, which is exactly why this shipped) - **The route regression test was confirmed non-vacuous**: neutralizing the fix fails the two "now retryable" cases while the three live-merge-protection cases still pass, proving they guard real behavior rather than the new code. - Regression tests assert the invariant across every surface per *Fix the Invariant, Not the Repro*: all five `ACTIVE_MERGE_STATUSES` (a merger can die in any phase, not just the reported `landing`), both live-merge signals, boundary conditions, fail-closed paths, and that pre-existing retry paths are unchanged. Test files carry the required `## Symptom Verification` and `## Surface Enumeration` sections. 🤖 Generated with [Claude Code](https://claude.com/claude-code) <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **Bug Fixes** * AI merge rejections now reliably include concrete, correctly ordered reasons, even when provided before the verdict line. * Manual retry can recover tasks stuck in stale merge-processing states. * Retry is still blocked for tasks tied to active merge activity or recently updated/advancing merges. * Existing failed-merge retry behavior remains unchanged. * **Reliability** * Improved shared handling of “orphaned” merge-active detection across the engine and dashboard. * **Tests** * Added/expanded coverage for merge-active staleness, retry eligibility, and verdict/reason parsing. <!-- end of auto-generated comment: release notes by coderabbit.ai --> --------- Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
||
|
|
959a7877c8 |
FN-8020: harden agent heartbeat health classification
Validate the four-interval heartbeat grace window and classify invalid persisted heartbeats safely. - Cover reported field heartbeat ages in dashboard and engine health checks. - Mark unparseable heartbeat timestamps as unresponsive and clamp future timestamps to fresh. - Align dashboard health documentation with the existing four-interval grace window. Files changed: .../app/utils/__tests__/agentHealth.test.tsx | 48 +++++++++++++++++++++- packages/dashboard/app/utils/agentHealth.tsx | 26 ++++++++++-- .../src/__tests__/heartbeat-executor.test.ts | 31 ++++++++++++++ 3 files changed, 100 insertions(+), 5 deletions(-) Fusion-Task-Id: FN-8020 Fusion-Task-Lineage: 2bc0df78-d68c-489b-8bfb-9b09da10cdfa Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai> |
||
|
|
9d791b3bf1 |
FN-8009: preserve approved plans through prompt hygiene
Keep manually approved plans idempotent when deterministic prompt hygiene is applied. - Document normalized fingerprint comparison at the approval gate - Cover approval reuse after Original Description and Frontend UX injection Files changed: packages/engine/src/__tests__/triage.test.ts | 37 ++++++++++++++++++++++++++++ packages/engine/src/triage.ts | 8 ++++++ 2 files changed, 45 insertions(+) Fusion-Task-Id: FN-8009 Fusion-Task-Lineage: 8474bc13-61e0-421a-8e99-080f99382285 Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai> |
||
|
|
3dcb62f40f |
FN-8008: normalize plan approval fingerprints
Keep approval recovery idempotent when deterministic prompt hygiene is injected. - Normalize plan approval fingerprints around Original Description and Frontend UX sections. - Preserve re-approval for operator-authored plan changes and cover recovery behavior. - Document the normalization contract and add a patch changeset. Files changed: .changeset/fn-8008-plan-approval-fingerprint.md | 7 +++ docs/workflow-steps.md | 2 +- packages/core/src/__tests__/plan-approval.test.ts | 53 +++++++++++++++- packages/core/src/plan-approval.ts | 73 ++++++++++++++++++++++- packages/engine/src/__tests__/triage.test.ts | 45 ++++++-------- packages/engine/src/triage.ts | 40 ++----------- 6 files changed, 153 insertions(+), 67 deletions(-) Fusion-Task-Id: FN-8008 Fusion-Task-Lineage: 9c0f415d-662a-455a-a4bd-b873307e53bc Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai> |
||
|
|
402b3a91fa |
fix(FN-8004): treat heartbeat soft-delete races as benign instead of stranding agents
A task soft-deleted concurrently with a heartbeat-driven moveTask raised TaskDeletedError from the engine's own board path, leaving the agent in `error` with a non-empty lastError and requiring a stop/start cycle to recover. The race is benign by construction: the task is gone, so the move is a no-op. The heartbeat now classifies it via isConcurrentSoftDeleteRaceError (matching the canonical message and serialized/typed forms), keeps the agent active, clears stale error/recovery state, and emits agent:heartbeat-move-skipped-soft-delete with ids/counts-only metadata. Concurrent operator pauses are preserved. Squash-merged by hand from fusion/fn-8004. The engine's AI merge approved this content twice (squash a3a3cc6a8) but could not land it: main advances every ~8 minutes and each merge cycle took ~10, so every attempt lost to a concurrent advance and rebuilt. Each cycle also burned a corrective pass on a first-pass review rejection with no stated reason — the issue #1946 class of bug that this task's own report cites as a sibling. Reconciled against #2157, which refactored transient-error-detector.ts: the new classifier coexists with the extracted transient-error-patterns.ts leaf. Verified on the merged tree — 123 tests green across FN-8004's suites and #2157's, engine typecheck clean. Fusion-Task-Id: FN-8004 Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
||
|
|
08a10bf486 |
fix(FN-8006): back off and pause Plan Review on provider rate limits
A rate-limited Plan Review re-ran every 30s for hours (~1,900 requests
per 5h window, reviewerFallbackRetryCount observed past 100), which is
the request volume that trips a provider's low-interactivity throttle —
so the retry storm prolonged the very outage it was retrying.
Root cause: runPlanReviewBeforeExecution catches every reviewStep throw
inline to keep triage alive, which converts them all to an UNAVAILABLE
verdict. That laundering had two consequences the earlier fixes missed:
FN-8006 terminalized RetryStormError and the reviewer started throwing
ReviewerProviderError for 429s, but a ReviewerProviderError still landed
in the UNAVAILABLE park — a FIXED 30s nextRecoveryAt with no attempt
counter and no cap. The reviewer's own escalation contract ("escalate so
UsageLimitPauser pauses every lane") held only on the executor path,
because the inline catch hid the error from triage's usage-limit handler
in specifyTask.
- triage: fire usageLimitPauser.onUsageLimitHit for usage-limit reviewer
failures, so a 429 pauses every lane instead of re-parking one task.
- triage: re-park via computeRecoveryDecision (60s/120s/240s, ±10%
jitter) and terminalize at MAX_RECOVERY_RETRIES. A reviewer that never
yields a verdict is a real failure and must surface, not spin.
- triage: clear the borrowed recoveryRetryCount budget on any real
verdict, so surviving an outage cannot shorten the executor's later
transient budget.
- core: RetryStormError takes an optional cause, surfaced as
underlyingError in serializeRetryStormError and folded into the
message, so a cap no longer masks the real error. recordRetry threads
it from the reviewer's error path.
Surface enumeration: the park is driven by a thrown provider error, a
thrown generic error, and a plain UNAVAILABLE verdict with no throw.
All three are covered — a repro pinned only to the reported 429 would
leave the other two spinning on the old fixed timer.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
|
||
|
|
71dd191c7c |
FN-8006: terminalize Plan Review retry storms
Plan Review now fails tasks when reviewer fallback retry limits are exceeded. - Detect RetryStormError from Plan Review workflow execution - Serialize the terminal retry error, clear recovery scheduling, and preserve workflow results - Add retry-storm regression coverage, architecture guidance, and a patch changeset Files changed: .changeset/fn-8006-plan-review-retry-storm.md | 7 ++++ docs/architecture.md | 2 +- packages/engine/src/__tests__/triage-plan-review-unavailable-retry.test.ts | 47 +++++++++++++++++++++- packages/engine/src/triage.ts | 33 +++++++++++++++ 4 files changed, 87 insertions(+), 2 deletions(-) Fusion-Task-Id: FN-8006 Fusion-Task-Lineage: 932e7930-2069-4b0c-9cd1-9db39c2de5a3 Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai> |
||
|
|
cae7847085 |
fix(FN-8004): retry ACP provider blips in auto-merge instead of parking failed (#2157)
## What happened
FN-8004's implementation work finished and passed review. The auto-merge
then failed with `Grok ACP turn failed: Internal error` — a ~20 second
provider blip — and the task was parked `status: "failed"` with 8 files
of complete, reviewed work stranded on its branch.
The park is the interesting part: `status: "failed"` is precisely what
tells recovery to stop. So a misclassification here isn't a missed
retry, it's **terminal**. Both recovery paths were disabled by the same
wrong verdict:
- `maybeRetryTransientMerge` (inline, 3 retries w/ backoff) — never
fired once (`mergeTransientRetryCount: 0`).
- `recoverTransientMergeFailures` (self-healing sweep, exists exactly to
rescue parked in-review tasks) — skipped it, gated on the same
classifier.
## Three defects fixed
**1. No AI-provider failure class existed.** The AI merge drives a real
LLM turn, but `classifyTransientMergeError` only modeled git/lease/spawn
faults. Adds `ai-provider-turn-failure`.
**2. ACP dropped the error detail.** `promptAcpSession` rethrew the SDK
error unchanged, discarding the JSON-RPC `code`/`data` — the only
evidence the fault was provider-side. ("Internal error" is just the
standard text for `-32603`.) It now preserves them, keeping the original
as `cause`:
```
Internal error (acp rpc code -32603, retryable)
```
Classification anchors on that envelope, **not** on the bare `"Internal
error"` — matching that unanchored would disguise genuine application
defects as retryable blips. Only provider-fault codes (`-32603`,
`-32000`..`-32003`) are retryable; caller-fault codes
(`-32600`..`-32602`) stay permanent, since retrying just repeats the
failing call.
**3. Sweep/inline asymmetry** (found while tracing; latent and
unreported). The inline gate accepted `isTransientError(msg) ||
classify(msg)`, but the sweep consulted **only** the classifier. So
`ECONNRESET` / `socket hang up` during a merge earned inline retries and
then went **invisible to the sweep** once parked — stranded forever. The
classifier now delegates to `isTransientError`, so both gates agree by
construction.
To keep that delegation from importing the detector's
`usage-limit-detector → logger` chain (the chain FN-5627 split the
classifier out to avoid, which would break
`notification-service.test.ts`'s partial `vi.mock`), the pure predicates
moved to the import-free leaf `transient-error-patterns.ts`, re-exported
from `transient-error-detector.ts`. All 13 exports preserved, verified
programmatically.
## Loosened budgets
Per request, so more self-heals. Both apply **only** to errors already
proven transient; the ceiling and
`merger:transient-failure-budget-exhausted` audit path remain.
| Budget | Before | After |
|---|---|---|
| `MAX_AUTO_MERGE_TRANSIENT_RETRIES` | 3 | 5 (backoff
5s/10s/20s/40s/80s) |
| `MAX_TRANSIENT_MERGE_RECOVERIES` | 2 | 5 |
The bump broke two suites that had hardcoded the old `3`. Rather than
swap in another magic number, both now derive the cap from the constant
so future tuning doesn't re-break them.
## Verification
- `pnpm test:gate` green · `pnpm lint` clean · engine + ACP typecheck
clean · `pnpm verify:fast` PASS (5/5)
- ACP plugin 230 tests green · Grok plugin 64 green · engine
transient/merge suites 136 green
- Regression tests assert the **invariant across every surface** (per
*Fix the Invariant, Not the Repro*), not just the reported Grok string:
both ACP runtime prefixes, all retryable/non-retryable rpc codes, both
SDK error shapes, network delegation, class-ordering, and negative cases
proving bare `"Internal error"` and real defects stay permanent.
- A test caught a genuine bug in my own code mid-review (nested-shape
message shadowing), now fixed.
- `notifier.test.ts > "awaiting approval"` fails — **confirmed
pre-existing on clean main**, unrelated.
## Note
FN-8004's own branch (`fusion/fn-8004`) is still unmerged and its work
looks complete. Once this lands, its merge should be retried separately.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
---------
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
|
||
|
|
0b332816d5 |
FN-7986: raise plan review replan cap to 8
Allow more automatic Plan Review revisions before escalating tasks for human approval. - Raise the consecutive REVISE replan cap from 3 to 8. - Cover the seven- and eight-revision boundaries in triage tests. - Add a patch changeset describing the revised default. Files changed: .changeset/fn-7986-plan-review-cap.md | 7 +++++++ .../triage-plan-review-replan-cap.test.ts | 23 +++++++++++++--------- packages/engine/src/triage.ts | 8 ++++---- 3 files changed, 25 insertions(+), 13 deletions(-) Fusion-Task-Id: FN-7986 Fusion-Task-Lineage: 3b61f333-be9a-414a-bd27-aabdbb45caa0 Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai> |
||
|
|
753b1bb710 |
fix(engine): honor graph cancellation at the merge node
The merge node could not observe a graph abort. WorkflowPrimitiveContext carried no signal, so requestMerge raced the merge only against its own 30-minute GRAPH_MERGE_TIMEOUT_MS using a controller it owned. A hard-cancel (user cancel, engine restart, pause/resume) aborted the graph controller and the walk kept sitting inside the merge node for the full timeout. When the timeout finally fired it aborted the still-running AI merge -- surfacing as "Manual-merge failed: Request was aborted" -- and the walk reported value=merge-timeout for a cancellation it had missed half an hour earlier. An abort landing between merger-ai's `worktree: null` write and mergeConfirmed then stranded the card as no-worktree-no-merge-confirmed. Thread the graph AbortSignal from WorkflowNodeExecutionContext (where it already existed) through primitiveNodeContext/primitiveContextForNode into the primitives, and honor it on both merge surfaces: - requestMerge fails fast when the walk is already cancelled, before ensureWorkflowMergeBoundaryTask mutates the row or the requester enqueues a merge, and links the graph signal into its timeout controller via AbortSignal.any -- raced separately so the walk returns on the abort rather than waiting on a requester that may never settle. - The legacy merge seam had the identical unguarded race and gets the same treatment. The timeout stays: it bounds a wedged merge queue, which is a different failure from cancellation. Both signals must stay live -- dropping either silently restores the stall with no type error. Cancellation returns a distinct `merge-cancelled` rather than reusing merge-timeout. Returning `data.status: "failed"` would let classifyMergeFailure read the unknown reason as merge-failed and route the cancellation into bounded auto-merge retry, re-requesting the merge the operator just cancelled. Regression test covers both merge surfaces, both cancel timings (pre-flight and mid-flight), the no-signal back-compat path, the signal plumbing itself, and the classification boundary. Verified by removing the fix: 7 of 9 cases fail, with the mid-flight cases hanging until timeout. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
||
|
|
40ae6ddb3a |
fix(FN-8024): stop logging skipped stale triage recovery writes
Skipping a stale planning-state write is the expected outcome of a normal scheduler advancement, not an anomaly, so the warn was pure log noise. Behavior is unchanged; only the two planLog.warn emissions are removed. Fusion-Task-Id: FN-8024 Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
||
|
|
0e84731d8a |
fix(FN-7965): let the overseer see executor-stage failures
`deriveSignalAndSources`'s executor branch never read `task.status`, so a row parked `status: "failed"` — e.g. the terminal fn_task_done refusal/invariant park — reported `signal: "progressing"` with the reason "Task is actively executing in-progress work". The overseer observed a dead task as healthy and took no action. `failed` was only ever derived for the merger/pull-request stages, so the sole backstop was the FN-7743 2h stall proxy firing hours later. This is exactly what FN-7965's audit trail shows: every intervention on a terminally-parked task was action="observe", reason="Task is actively executing in-progress work". Report `failed` so recovery engages on the next poll. This adds no new policy: a failed executor observation already routes to `retry_step` (executor sources are `agent-log`, never an ERROR_SOURCE_KIND), bounded by PLANNER_RECOVERY_MAX_ATTEMPTS and escalated on exhaustion. Precedence and dedup preserved: `paused` still wins, so an operator/user-paused row stays `blocked` and is never routed into autonomous recovery; and the reason is a constant (never interpolating task.error/status) so the FN-7577 `stage|signal|reason` feed dedup still suppresses repeat observations. Verified: the repro test fails with the branch disabled; paused-precedence, healthy-card (FN-7577) and dedup guards added; overseer/recovery surfaces 93 passed + core planner-recovery 20/20; engine + dashboard typecheck clean; `pnpm test:gate` green (294+122+63). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
||
|
|
214af98591 |
FN-7977: hold Plan Review provider failures without replan regression
Prevent provider, model, transport, and abort failures from bouncing tasks back to planning after they enter execution. - Classify non-plan-defect Plan Review failures and skip needs-replan handoff - Terminate graph traversal with plan-review-provider-failure-hold and retry in place - Guard triage recovery so advanced column/worktree/step state is never overwritten - Document planning-recovery no-regression invariant and add regression tests - Add patch changeset for the operator-facing fix Files changed: .changeset/fn-7977-planning-failure-no-regression.md | 7 ++ docs/architecture.md | 1 + docs/workflow-steps.md | 2 +- packages/engine/src/__tests__/replan-target.test.ts | 17 +++- packages/engine/src/__tests__/transient-error-detector.test.ts | 32 +++++- packages/engine/src/__tests__/triage.test.ts | 110 +++++++++++++++++++++ packages/engine/src/__tests__/workflow-graph-optional-group.test.ts | 46 ++++++++- packages/engine/src/__tests__/workflow-graph-optional-step-fix.test.ts | 36 +++++++ packages/engine/src/executor.ts | 62 +++++++++++- packages/engine/src/replan-target.ts | 22 +++++ packages/engine/src/transient-error-detector.ts | 37 +++++++ packages/engine/src/triage.ts | 73 +++++++++++--- packages/engine/src/workflow-graph-executor.ts | 45 ++++++++- 13 files changed, 466 insertions(+), 24 deletions(-) Fusion-Task-Id: FN-7977 Fusion-Task-Lineage: 6d62d3ca-c6f3-4d02-a377-d7fd59f0c0f9 Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai> |
||
|
|
f1b528f4c5 |
fix(FN-7965): honor terminal fn_task_done park instead of resurrecting the session
The in-session `fn_task_done` handler parks a task terminally (status=failed, worktree/branch/sessionFile cleared) once its refusal/invariant retry budget is exhausted. That write happens inside the live agent session, so the executor's no-fn_task_done retry loop never observed it and spawned a fresh session anyway. The retry completed, marked the task done, and dragged a worktree-less row into the pre-merge graph, where the first write-capable node failed on `no-worktree-for-write-node` — surfacing as a misleading "Workflow graph terminated with failure at node 'code-review-remediation'" instead of the real refusal. Observed on FN-7965 and again live on FN-7981. Re-read state at the top of the retry loop and honor the park. The status probe covers all three park sites (invariant-check, explicit refusal, implicit refusal) rather than the single reported repro. Deliberately not routed through the FN-4806 reclaim branch: its silent todo requeue would clear the park and, with the budget already spent, re-park on the next pickup in a todo->execute->park loop. The pre-existing reclaim probes could not catch this — they test `worktree === null`, but the store maps a cleared column to `undefined` (`task-store/serialization.ts`: `row.worktree || undefined`), so the existing test only passed because its mock returned a value production never emits. Tightening that probe regressed 7 fixtures and is left as separate work. Verified: new tests fail with the guard disabled; engine reliability surfaces show zero regressions vs baseline (17 pre-existing failures unchanged, 495->499 passing); engine-core gate suite 294/294. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
||
|
|
363916926d |
FN-7995: always persist tool_error detail for Activity feed diagnosis
Always persist bounded tool_error detail so the task Activity feed can surface underlying failure messages even when verbose tool-output persistence is off. - Keep tool args and successful tool_result detail opt-in via persistAgentToolOutput - Always include bounded tool_error detail in agent-log JSONL rows - Document diagnostic retention in types, agent-logger, and storage docs - Cover Activity reveal behavior and logger persistence with unit tests - Add patch changeset for operator-facing Activity error detail fix Files changed: .changeset/fn-7995-tool-error-detail.md | 7 ++++ docs/storage.md | 1 + packages/core/src/agent-log-constants.ts | 4 +++ packages/core/src/types.ts | 10 ++++-- .../app/components/__tests__/TaskChatTab.test.tsx | 42 ++++++++++++++++++++++ packages/engine/src/__tests__/agent-logger.test.ts | 41 ++++++++++++++++++--- packages/engine/src/agent-logger.ts | 9 ++--- 7 files changed, 104 insertions(+), 10 deletions(-) Fusion-Task-Id: FN-7995 Fusion-Task-Lineage: 0fa063df-58b1-4991-a0d9-e8a77181d32a Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai> |
||
|
|
dc7bb40948 |
FN-7990: share worktree classifier so Code Review acquires a worktree
Unify write-capability classification so graph preparation acquires a worktree for inline-fix Code Review before runtime runs, eliminating the immediate no-worktree-for-write-node failure. - Add shared workflowNodeRequiresWorktree helper for preparation and runtime - Plumb optional-group context and reviewerInlineFixes into graph preparation - Acquire/reuse/reacquire worktrees for write-capable inline review nodes - Keep Plan Review and disabled inline fixes read-only - Add regression tests and a patch changeset Files changed: .changeset/fn-7990-code-review-worktree.md | 7 ++ .../__tests__/ce-workflow-step-executor.test.ts | 97 ++++++++++++++++++++++ .../workflow-node-execution-needs.test.ts | 47 +++++++++++ packages/engine/src/executor.ts | 32 +++---- packages/engine/src/workflow-graph-executor.ts | 52 ++++++++---- .../engine/src/workflow-node-execution-needs.ts | 46 ++++++++++ 6 files changed, 243 insertions(+), 38 deletions(-) Fusion-Task-Id: FN-7990 Fusion-Task-Lineage: f5d19181-0b98-4827-8adb-069f7dc05c03 Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai> |