44e20274d7422fbbef4e13b4eb2957d486e685c5
3351 Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
e18c3b4584 |
FN-8262: route OMP models through ACP runtime
Route omp-cli model selections to the bundled OMP ACP runtime instead of pi registry resolution. - Derive and validate OMP runtime routing for primary and fallback model selections. - Preserve mock and test-mode short-circuits and provide actionable plugin remediation. - Add OMP routing regression coverage and a patch changeset. Files changed: .changeset/fn-8262-omp-model-routing.md | 7 + .../agent-session-helpers-omp-routing.test.ts | 218 +++++++++++++++++++++ packages/engine/src/agent-session-helpers.ts | 87 +++++++- 3 files changed, 306 insertions(+), 6 deletions(-) Fusion-Task-Id: FN-8262 Fusion-Task-Lineage: 7a77031f-8703-484e-9e53-a2ef097ec17e Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai> |
||
|
|
377cb9c90a |
FN-8258: complete PostgreSQL quarantine rescues
Complete PostgreSQL-backed rescue coverage while retaining archived shared-branch landing proof. - Preserve merge details when archiving and restoring tasks for branch-group promotion. - Migrate remaining quarantine tests and mocks to PostgreSQL-aware boundaries. - Remove rescued tests from the engine quarantine configuration and ledger. Files changed: .changeset/fn-8258-pg-quarantine.md | 7 +++ .../core/src/task-store/archive-lifecycle-2.ts | 1 + packages/core/src/task-store/remaining-ops-6.ts | 8 ++- packages/core/src/task-store/serialization.ts | 1 + .../__tests__/agent-tools-intake-column.test.ts | 26 ++++------ .../agent-workflow-tools-exposure.test.ts | 18 +++---- .../engine/src/__tests__/executor-task-done-invariant.test.ts | 33 ++++++------ .../engine/src/__tests__/executor-test-helpers.ts | 7 +++ .../src/__tests__/group-merge-coordinator.test.ts | 43 ++++++++++------ .../hybrid-executor-multi-node-routing.test.ts | 5 ++ .../mission-factory-parity.integration.test.ts | 2 +- .../engine/src/__tests__/routine-runner.test.ts | 56 +++++++++++++-------- .../self-healing-meta-archive-guards.test.ts | 28 +++++------ .../src/__tests__/triage-token-usage.test.ts | 58 +++++----------------- .../__tests__/workflow-graph-task-runner.test.ts | 16 +++--- packages/engine/src/hybrid-executor-gate.ts | 8 ++- packages/engine/vitest.config.ts | 12 +---- scripts/lib/test-quarantine.json | 52 +------------------ 18 files changed, 165 insertions(+), 216 deletions(-) Fusion-Task-Id: FN-8258 Fusion-Task-Lineage: 121c2b52-ad50-4475-b925-7a36ecfaf28b Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai> |
||
|
|
2ab0413c07 |
fix: make OMP process lifecycle tests full-suite safe (#2290)
## Summary After #2289, Full Suite shard 4 still failed on the **OMP** twin of the Grok process-lifecycle stress test (`import("../index.js")` × 15 under shard transform load → 5s timeout). Apply the same fix class as grok-runtime: - Symbol.for exit reaper on `process-manager` - Stress test reimports that module - 15s timeout for cold transform ## Test plan - [x] Local OMP process-lifecycle green - [ ] PR gate - [ ] Post-merge Full Suite <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit - **Bug Fixes** - Improved cleanup of OMP ACP processes when the application exits. - Prevented duplicate exit handlers and excess listener growth during runtime reloads. - Preserved reliable process lifecycle behavior under repeated module loading. - **Tests** - Added lifecycle coverage for repeated process-manager reloads. - Optimized the stress test to complete more efficiently while retaining cleanup assertions. <!-- end of auto-generated comment: release notes by coderabbit.ai --> |
||
|
|
57120f3d29 |
test: preserve default getTask mock for DUPLICATE recovery (#2283)
## Summary Follow-up to #2275 review feedback: `getTask` mocks for DUPLICATE recovery tests now return a default current-task fixture for non-canonical IDs instead of `undefined`. ## Context CodeRabbit on #2275 noted that returning `undefined` for every id except the canonical task could break recovery if the subject task is re-fetched mid-path. ## Test plan - [x] `triage-split-into-subtasks-delete.test.ts` - [x] `triage-finalize-duplicate-lineage.test.ts` <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **Tests** * Improved duplicate-resolution test coverage by preserving task data during recovery and re-fetch scenarios. * Added validation for non-canonical task handling to prevent errors when duplicate tasks are resolved or split. <!-- end of auto-generated comment: release notes by coderabbit.ai --> |
||
|
|
5c7ed8b26f |
chore(release): v0.72.0
Version bump via changesets. |
||
|
|
38c6fdc60d |
fix: green full-suite after mock and stacking drift (#2287)
## Summary Restacks onto latest main after #2285 and clears the remaining Full Suite red classes from run [29633869887](https://github.com/Runfusion/Fusion/actions/runs/29633869887) (post-#2285): - **Shard 1:** `agent-skills-flow` vitest TDZ — hoist `mockFiles` via `vi.hoisted` (same class as skill-resolver in #2285) - **Shard 2/3:** incomplete mocks after product drift - `isFullScreenSheetViewport` / `isShortViewport` on viewport mocks (without overriding dynamic mobile helpers) - `fetchCodebaseMetrics` on Command Center `api/legacy` mocks - `fetchSettings` on `agent-modals-mobile` api mock - **Shard 3:** PlanningMode `ui-interactions` race — sync-settle `fetchGlobalSettings` (FN-8245 pattern from planning-flow) - **Shard 3:** settings search drift guard — inventory `SettingsFieldRow` `htmlFor` keys (`mobileNavPrimaryItems`) - **Shard 2:** FloatingWindow shared-stack product bug — only reclaim z-index on hidden→visible (not every mount effect), so last-mounted utility stays on top - **Shard 4:** grok process-lifecycle timeout under shard load — prove bound with 5 reimports instead of 15 ## Test plan - [x] `agent-skills-flow.test.ts` green - [x] `process-lifecycle.test.ts` green - [x] FileBrowserModal, FloatingWindowStack.cross-type, agent-modals-mobile, settings-search-index, SystemControlsArea, PlanningModeModal.ui-interactions + planning-flow (210 tests) green - [ ] PR merge gate (Lint/Typecheck/Build/Gate) - [ ] Post-merge Full Suite on `main` green <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **Bug Fixes** * Improved floating-window stacking so reopened or interacted windows appear in the correct order. * Restored consistent layering between floating windows and expanded dock modals. * **Tests** * Updated automated coverage for viewport behavior, codebase metrics, settings search indexing, and process lifecycle scenarios. * Improved test reliability and consistency across responsive layouts and modal interactions. <!-- end of auto-generated comment: release notes by coderabbit.ai --> |
||
|
|
1afce9532a |
fix: green full-suite after product and CI PG drift (#2285)
## Summary Main Full Suite was red again after release/desktop workflow drift, engine mock gaps, mission landed-SHA gating, and compound-engineering PG admin auth on GHA (`USER=runner`). ## Fixes | Area | Failure | Fix | |------|---------|-----| | desktop `release-workflow` | expected old `find artifacts -type f` | assert pruned collect + `release-files/*` | | `step-session-executor` | missing `resolveExecutorFallbackThinkingLevel` | mock export | | tool-availability tests | empty tools (cascade from above) | fixed by mock | | `skill-resolver` | TDZ on `mockFiles` during import | `vi.hoisted` filesystem state | | `merge-error-recovery` | enqueue no-op when not started | set `started=true` | | mission behavioral posture | `blocked` (no landed SHA / git probe) | `mergeDetails.commitSha` + staleness stub | | GraphTaskNode | missing `useOptionalToast` | mock both toast exports | | CE `pipeline-store.pg` | psql as `runner` | admin via `FUSION_PG_TEST_URL_BASE` | ## Test plan - [x] step-session-executor, skill-resolver, merge-error-recovery, mission-validator-behavioral-posture (203) - [x] release-workflow (10) - [x] `pnpm --filter @fusion/engine test:core` (294) - [ ] Full Suite (non-blocking) after merge <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit - **Bug Fixes** - Improved desktop release artifact collection by pruning nested `runtime` and `migrations` directories and consistently staging release uploads via a dedicated `release-files` mapping. - **Tests** - Enhanced engine merge error-recovery coverage and mission validator behavioral posture setup. - Improved test reliability by synchronizing mocked filesystem state, executor fallbacks, and toast hook variants. - Updated Postgres test harness/admin commands to use a configurable base URL; refined related Windows changeset description. <!-- end of auto-generated comment: release notes by coderabbit.ai --> |
||
|
|
7dda1aa3f5 |
chore(release): v0.71.0
Version bump via changesets. |
||
|
|
e31223c25f |
chore(release): v0.70.2
Version bump via changesets. |
||
|
|
eccb115c7e |
chore(release): v0.70.1
Version bump via changesets. |
||
|
|
e24f765495 |
FN-8252: rescue quarantined engine tests
Restore non-mechanical engine coverage with PostgreSQL-safe test fixtures and awaited overseer audit writes. - migrate eligible engine tests to shared PostgreSQL harnesses and restore their Vitest coverage - harden mission and advisory reporting paths for async persistence and observable failures - await the production planner-overseer audit callback and verify the start() wiring preserves persistence Files changed: .../__tests__/mission-autopilot-end-to-end.test.ts | 27 ++-- .../engine/src/__tests__/mission-autopilot.test.ts | 4 +- .../planner-overseer-intervention-wiring.test.ts | 39 +++--- .../engine/src/__tests__/project-engine.test.ts | 138 ++++++++++++++++----- .../unlinked-missions-advisory-reporter.pg.test.ts | 51 ++++++++ .../unlinked-missions-advisory-reporter.test.ts | 20 ++- packages/engine/src/mission-execution-loop.ts | 27 ++-- packages/engine/src/project-engine.ts | 41 +++--- .../src/unlinked-missions-advisory-reporter.ts | 23 ++-- packages/engine/vitest.config.ts | 6 +- scripts/lib/test-quarantine.json | 27 +--- 11 files changed, 260 insertions(+), 143 deletions(-) Fusion-Task-Id: FN-8252 Fusion-Task-Lineage: 4f86ce7e-11a2-4704-a5d1-00e0a8c1448e Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai> |
||
|
|
9cafa045df |
fix(pr-merge): resolve repo from the project checkout, not process.cwd(), in PR-mode auto-merge (#2281)
## Summary In a centrally-installed, multi-project Fusion server (one process serving several repos, `process.cwd()` = the install dir, not any repo), every task under `mergeStrategy: "pull-request"` fails at the auto-merge stage with: ``` Could not determine repository. Specify owner/repo in params or run from a git repository with a GitHub remote. ``` PR creation from the dashboard and status polling work; only the engine's automatic PR path fails. This is the **non-workspace sibling of #1924** (FN-7610 routed workspace-mode tasks to direct merge but does not cover regular multi-project tasks) and the completion of #1797/FN-7133 (which fixed only the `getPrMergeStatus` arguments). ## Root cause `GitHubClient.resolveRepo()` (`packages/dashboard/src/github.ts`) falls back to a cwd-less `getCurrentRepo()` — i.e. `git remote get-url origin` in `process.cwd()` — whenever a PR method is called without explicit `owner`/`repo`. The engine merge path already resolves the correct repo from the per-project cwd (`prRepo = getCurrentRepo(cwd)`, FN-7133) but only threaded it into `getPrMergeStatus`. Every other GitHub call omitted it: - `processPullRequestMergeTask`: `findPrForBranch` / `createPr` / `mergePr` on both the per-task and shared-branch-group paths - `createGroupPrCallback` (group-PR promotion): `findPrForBranch` / `createPr` - `createPrNodeGithubOps` (`pr-create`/`pr-merge` workflow nodes): cwd-less `getCurrentRepo()` persisted `entity.repo` as `""` (poisoning the downstream `splitRepoSlug` consumers), and the git push/`createPr`/`mergePr` ran against `process.cwd()` - the engine's review-response run (`buildRespondCallback`): `respondOps.getCwd` collapses to `process.cwd()` because no CLI composition site wires `getTaskWorktree`, so its git ops and response agent ran outside the project repo In a central install the fallback throws; worse, if `process.cwd()` happens to be inside some *other* git repo, it silently targets the **wrong repository**. ## What changed - `fix(pr-merge): thread repo identity into PR auto-merge GitHub calls` — widens the CLI-local `GitHubOperations` interface (optional `owner`/`repo`, already accepted by `GitHubClient`'s `FindPrParams`/`CreatePrParams`/`MergePrParams`) and passes `prRepo` at all six call sites in `processPullRequestMergeTask`. - `fix(pr-merge): resolve group-PR repo from project cwd in createGroupPrCallback` — resolves via `getCurrentRepo(cwd)` from the callback input (same T4 pattern as `syncGroupPrCallback`) with a loud failure instead of a silent wrong-repo fallback. - `fix(pr-merge): resolve PR-node repo from task worktree instead of process cwd` — `resolvePrSource` resolves from `task.worktree`, git ops run in `getTaskWorktree(...) ?? task.worktree ?? process.cwd()`, and `createPr`/`mergePr` pass `owner`/`repo` parsed from `entity.repo`. - `fix(pr-merge): resolve review-response run cwd from the task worktree` — the engine owns the store, so `buildRespondCallback` prefers the task's recorded `worktree` for the response run's git ops + agent, keeping `ops.getCwd` as the single-project fallback (defensive against structural `PrNodeStore`s without `getTask`). - Changeset (`@runfusion/fusion` patch, structured body) included. Deliberately **not** done: a constructor-scoped default repo on `GitHubClient` — one client instance is shared across all projects in a central install (`serve.ts`/`daemon.ts`/`dashboard.ts`), so per-call `owner`/`repo` is the only correct scope. ## Testing - New regression tests simulate the central-install topology (`getCurrentRepo` mocked as `(cwd?) => cwd ? repo : null`, exactly the failing environment) and drive the merge flow end-to-end on the per-task path, the shared-branch-group path, `createGroupPrCallback`, and all three `createPrNodeGithubOps` ops, asserting every GitHub call carries explicit `owner`/`repo` (45 tests in `packages/cli/src/commands/__tests__/task-lifecycle.test.ts`, all green). - `packages/engine/src/__tests__/pr-respond-cwd-resolution.test.ts` covers the respond-run cwd: worktree preferred, `ops.getCwd` fallback when the task has no worktree, when the lookup fails, and when a structural store has no `getTask`. - Existing exact-argument assertions were extended to the new call contract (no assertions weakened or removed). - `pnpm lint`, `pnpm typecheck`, and `pnpm build` green locally; `pnpm test:gate`'s engine-core suite green (294/294) — its PostgreSQL-backend lane needs local PG credentials this environment lacks, so that lane defers to CI. `pnpm verify:fast` (scoped typecheck/build + CLI build + boot smoke) also passes. ## Repro 1. Install the CLI centrally; run the server from a dir that is not a git repo, serving ≥1 project with a GitHub `origin` and `mergeStrategy: "pull-request"`. 2. Run a task to completion and let it reach the merge stage. 3. Before this fix: the auto-merger throws `Could not determine repository …` (tasks with a persisted PR poll fine but never merge). Merging the same task from the Pull Requests tab succeeds, because the dashboard route resolves the repo explicitly (`parseBadgeUrl(...) ?? getCurrentRepo(rootDir)`). Full analysis: https://github.com/Tchori-Labs/Fusion/issues/4 --- Developed with Claude (co-authored on all commits). https://claude.ai/code/session_01ChEa8SHFYNAzjCdFbwFMfh <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit - **Bug Fixes** - Resolved pull request auto-merge failures in centrally installed, multi-project deployments. - Ensured explicit repository context (`owner/repo`) is used for pull request lookup, creation, and merging throughout the merge workflow. - Improved pull request response handling to prefer the task worktree for working-directory resolution, with safe error behavior when task details are unavailable. - **Tests** - Expanded coverage for multi-repository merge workflows and worktree-based repository/cwd resolution in PR response handling. <!-- end of auto-generated comment: release notes by coderabbit.ai --> --------- Co-authored-by: Claude <noreply@anthropic.com> |
||
|
|
3fcab9fc1d |
fix: green full-suite after main product drift (#2275)
## Summary Follow-up after #2266: full suite on latest `main` still failed on i18n parity and engine product-drift tests (duplicate flagging, mission validator signature, worktree path reservation, pi ModelRuntime, assigned-agent PG layer). ## Changes - **i18n**: add `listView/tasks.statusReplan` and mobile-nav settings keys to es/fr/ko/zh-CN/zh-TW - **triage**: opt-in `triageDuplicateResolution: "delete"` coverage + default prompt/flag path; stub `recordActivity` - **MCP PR response**: stub `getTask` for merger model resolution - **pi-layers**: mock `ModelRuntime.create` (FN-8142) - **assigned-agent**: stub `getAsyncLayer` for authoritative AgentStore fallback - **mission validation**: `startValidatorRun(..., taskId)` + `runValidation` `{ result, inspection }` shape - **worktree acquisition**: real temp roots for path reservation; probe-aware worktrunk failure fixture - **useBlockerFanout**: read `MAX_AUTO_MERGE_RETRIES` from `self-healing-constants.ts` (wave-8 peel) ## Test plan - [x] Targeted engine suites above (48 tests) - [x] i18n parity + gate coverage - [x] useBlockerFanout - [x] `pnpm test:gate` - [ ] Full Suite (non-blocking) on PR <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **Localization** * Added “Replan”/replanning status labels across Spanish, French, Korean, Simplified Chinese, and Traditional Chinese. * Completed settings mobile navigation translations, including primary items controls and reordering options. * **Tests** * Expanded coverage for validation recovery and task forwarding behavior. * Improved duplicate-resolution scenarios (including activity recording and lineage deletion behavior). * Increased test reliability for worktree acquisition by using temporary filesystem roots. <!-- end of auto-generated comment: release notes by coderabbit.ai --> |
||
|
|
f5538e6253 |
chore(release): v0.70.0
Version bump via changesets. |
||
|
|
703488f0f3 |
fix(FN-8142): restore Anthropic OAuth refresh
Use the Claude OAuth client identity issued by the login flow and lock the request contract down with a regression assertion. |
||
|
|
62f121e0c9 |
FN-8247: stop session advisor with oversight
Stop now persists session-advisor disablement and reflects inherited advisor state in task details. - Persist an explicit advisor-off override and clear its live runtime when stopping oversight. - Resolve workflow-level advisor defaults for task-detail icons and toggle behavior. - Cover stop cleanup and desktop/mobile oversight state transitions with regression tests. - Document the combined stop contract and add a patch changeset. Files changed: .changeset/fn-8247-session-advisor-stop-and-icon.md | 7 ++ docs/dashboard-guide.md | 6 +- packages/dashboard/app/components/TaskDetailModal.tsx | 86 +++++++++++------ packages/dashboard/app/components/__tests__/TaskDetailModal.oversight-controls.test.tsx | 104 +++++++++++++++++++++ packages/dashboard/app/components/__tests__/TaskDetailModal.oversight-mobile.test.tsx | 62 ++++++++++++ packages/engine/src/__tests__/project-engine-stop-overseer-session-advisor.test.ts | 62 ++++++++++++ packages/engine/src/project-engine.ts | 13 ++- 7 files changed, 310 insertions(+), 30 deletions(-) Fusion-Task-Id: FN-8247 Fusion-Task-Lineage: dcb67af7-9a36-4b25-bafa-84722fe158a0 Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai> |
||
|
|
600e9c9ecc |
FN-8242: fix triage duplicate-finalization test mock
Make triage duplicate-finalization tests provide the activity recorder used by the implementation. - Add an awaited recordActivity mock to the shared TaskStore fixture. - Select delete resolution in the reviewer-outage retry scenario so it reaches deleteTask. Files changed: packages/engine/src/__tests__/triage.test.ts | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) Fusion-Task-Id: FN-8242 Fusion-Task-Lineage: 8f9fd4b6-8071-47f3-8091-747884762ec9 Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai> |
||
|
|
0b6957a1b2 |
FN-8241: log Plan Review revisions distinctly
Classify requested Plan Review changes as normal specification revision activity rather than workflow failures. - Replace failure log actions for deterministic and reviewer-requested revisions - Preserve failed workflow-result state for replan and recovery processing - Cover revision logging across deterministic, reviewer, and retry paths Files changed: .../triage-review-spec-external-integration.test.ts | 5 ++++- packages/engine/src/__tests__/triage.test.ts | 11 +++++++++++ packages/engine/src/triage.ts | 14 ++++++++++++-- 3 files changed, 27 insertions(+), 3 deletions(-) Fusion-Task-Id: FN-8241 Fusion-Task-Lineage: 316370ac-36c2-45b5-944f-602375167cdd Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai> |
||
|
|
5d2c3be6a0 |
FN-8221: clear inactive planner overseer state
Clear retained planner overseer state when effective oversight is disabled. - Remove monitor, recovery, advisor, and dedup runtime for oversight-off tasks. - Suppress stale oversight-off Eye badges in task cards. - Cover cleanup and badge behavior with regression tests. - Document the runtime snapshot invariant and add a patch changeset. Files changed: .changeset/fn-8221-overseer-badge-oversight-off.md | 7 ++ docs/architecture.md | 4 + packages/dashboard/app/components/TaskCard.tsx | 9 +- .../app/components/__tests__/TaskCard.test.tsx | 20 ++++ .../__tests__/planner-overseer-off-cleanup.test.ts | 119 +++++++++++++++++++++ packages/engine/src/project-engine.ts | 12 +++ 6 files changed, 170 insertions(+), 1 deletion(-) Fusion-Task-Id: FN-8221 Fusion-Task-Lineage: 01c9d838-fbe4-4d34-8eb5-d735cf35e581 Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai> |
||
|
|
5acea6c0cf |
fix(pg): route residual SQLite-stub store paths through the async data layer (#2273)
## Summary An audit of the SQLite→PostgreSQL store migration found data-store paths still reaching the removed SQLite stub in backend (PG) mode. In backend mode `store.db`/`getDatabase()` throw the removed-SQLite error, so each of these either threw on every run or — worse — had the throw swallowed into a silent wrong result. This PR routes all of them through the `AsyncDataLayer` (and removes one dead primitive). ## The 6 live bugs fixed | Fix | Was | |-----|-----| | `executor.ts` authoritative assigned-agent fallback now inherits the TaskStore `asyncLayer` | silently returned `null` → model drift to the pi built-in (the exact thing its comment guards) | | `pruneAgentLogFilesAsync` replaces the sync self-healing prune call | threw `SQLite Database is not available` every maintenance sweep → agent-log pruning never ran | | `cleanupOrphanedMaterializedSteps` deletes PG `workflow_steps` rows on a failed create | swallowed the throw → leaked rows | | `deleteTaskBackendImpl` now runs the async mission feature/task-link unlink | PG hard delete left orphaned mission links | | `getWorkflowSettingsProjectId` returns `rootDir` in backend mode without touching the stub | swallowed throw for unscoped backend stores | | `fn plugin` unregistered-project fallback bootstraps a `CentralCore` `AsyncDataLayer` | layerless `PluginStore` threw in PG | ## The 4 latent traps, fixed properly - **`cleanupArchivedTasks`** — real async port (enumerate archived soft-deleted rows, guarantee cold snapshot, hard-delete project row + purge selection rows + rm dir). - **`deleteWorkflowStep`** — real async port (delete `workflow_steps` via the layer with `.returning()` to preserve the not-found contract). - **`applyTaskPatch`** — **removed** (zero-caller SQLite column-patch primitive with no backend analogue; impl + facade + import deleted). - **`AgentStore.importLegacyFileRuns`** — clean backend no-op (no legacy SQLite run-files exist in a PG deployment; its only `init()` caller early-returns in backend mode). ## Symptom Verification New PG regression suite `packages/core/src/__tests__/postgres/store-sqlite-residue-fixes.pg.test.ts` reproduces the original failures against real embedded Postgres and asserts they're gone: - orphaned `workflow_steps` are actually deleted (no swallowed throw) - `pruneAgentLogFilesAsync` resolves and prunes inactive-task log files - hard delete unlinks the mission feature from the task - `deleteWorkflowStep` removes the row / reports not-found - `cleanupArchivedTasks` hard-deletes the project row while retaining the cold snapshot ## Verification - `@fusion/core`, `@fusion/engine`, `@runfusion/fusion` typecheck clean - ~50 existing + 5 new PG tests pass; lint clean; changeset validates 🤖 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** * Prevented PostgreSQL backend maintenance from hitting removed legacy SQLite code paths, avoiding datastore failures and residue cleanup issues. * Fixed workflow-step deletion and “not found” behavior in backend mode. * Ensured backend hard-deletes correctly unlink related mission feature/task links and clean orphaned materialized steps. * Prevented legacy file-run imports from incorrectly reporting success in backend mode. * **New Features** * Added async agent-log pruning for inactive tasks and updated maintenance to use it. * **Tests** * Added PostgreSQL regression coverage for residue fixes and archive/workflow cleanup. * **Refactor** * Removed an unused task patch operation and updated task-store cleanup methods to be async where needed. <!-- end of auto-generated comment: release notes by coderabbit.ai --> --------- Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com> |
||
|
|
a7ab3ccb49 |
fix(heartbeat): stop zombie-timer audit churning live-but-skipping timers (#2271)
## Problem Permanent (durable) agent heartbeats went silent while the rest of the engine kept running. Investigation of the live DB showed **every** permanent agent's `heartbeatTimerRepair` metadata carrying `nonAdvancingEscalated: true` with **3–63 consecutive** zombie re-arms — the audit re-arming a timer every 60s for hours while emitting `heartbeat-rearm-nonadvancing-escalated` warnings that never recovered anything. ## Root cause The heartbeat trigger audit classified a "zombie" (dead) timer **solely from a stale `lastHeartbeatAt`**. But that column advances *only* on a successful `"ok"` delivery (`agent-store.ts` `recordHeartbeat`). It stays frozen whenever a heartbeat is intentionally skipped or no-op'd: - agent over budget / over budget threshold - `globalPause` / `enginePaused` - `skipHeartbeatWhenIdle` on an idle agent - idle "org" agents whose runs complete as `no_assignment_identity_run` In all of these the interval keeps firing perfectly — the timer is alive, delivery is just (correctly) skipped. Keying zombie detection off `lastHeartbeatAt` misread those healthy timers as dead, re-armed them every 60s, and escalated forever. Re-arming a live timer is a no-op, so the loop could never recover — it only produced churn and phantom warnings. ## Fix (the invariant) Key zombie detection off **whether the interval physically fired**, not whether delivery advanced. - New `lastTimerFireAtMs` map, stamped at the top of `onTimerTick` **before any gate** — a fired-but-skipped tick still counts as proof of liveness. - In the audit: a present + stale timer that fired within its stale window is **left untouched** (no re-arm, no escalation, non-advancing counter reset). Only a timer with **no recent fire** (a genuinely dead interval) falls through to the existing re-arm/escalation path. - Map cleaned up in `unregisterAgent()` / `stop()`. This preserves the FN-7645 zombie repair (a timer that stops firing goes stale in lockstep on both clocks and is still re-armed) and the FN-7939 watchdog, while eliminating the phantom churn for live-but-skipping timers. Why not "force a heartbeat" or "park the agent": forcing delivery would bypass budget/pause governance, and parking a healthy idle agent would be wrong. The correct action for a live-but-skipping timer is to leave it alone — its next real tick delivers once the skip condition clears. ## Tests - Rewrote the old `skipHeartbeatWhenIdle` test that codified the buggy escalation → now asserts a **live** idle-skipping timer is left untouched (no zombie re-arm, no escalation). - Added a budget/no-assignment surface: a live timer that dispatches but leaves `lastHeartbeatAt` frozen must not be misclassified. `heartbeat-scheduler.test.ts` 120/120; broader heartbeat + concurrency suites 349/349; `@fusion/engine` typecheck 0 errors. ## Review Self-reviewed at medium effort. Two acknowledged, bounded trade-offs (kept intentionally): a genuinely-dead-but-recently-fired timer's repair latency is bounded at ~2× interval (same as the original FN-7645 latency), and the escalation warning is suppressed for live timers (it only ever fired because of the churn this removes; per-tick error logs + a new "left live-but-skipping timer" log retain visibility). One trivial cleanup applied (single `Date.now()` sample). ## Notes - Engine is a private package → no changeset. - Complementary to a separate in-flight fix for the agentStore/scheduler-not-constructed bug (why heartbeats stopped *entirely*); this PR ensures that once the scheduler runs again, the audit stops the phantom churn/escalation. 🤖 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 heartbeat timer monitoring to distinguish healthy timers from genuinely stopped timers. * Prevented unnecessary timer re-registration and warning escalation when heartbeats are intentionally skipped due to idle, paused, budget-limited, or unassigned states. * Improved recovery when a replacement timer stops firing, ensuring it is detected and repaired reliably. <!-- end of auto-generated comment: release notes by coderabbit.ai --> --------- Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com> |
||
|
|
eb7d223a03 |
FN-8207: add task reassignment for delegated work
Correct delegation routing for duplicate tasks and enable explicit task owner reassignment. - Preserve requested assignee and todo routing on duplicate canonical tasks - Add governed fn_task_assign to engine, heartbeat, triage, workflow, and chat sessions - Cover assignment validation, truthful delegation responses, and tool availability Files changed: .changeset/fn-8207-delegate-assign.md | 7 ++ docs/agents.md | 6 ++ packages/core/src/types.ts | 1 + packages/core/src/usage-events.ts | 2 +- .../dashboard/src/__tests__/chat-manager.test.ts | 2 + packages/dashboard/src/__tests__/chat.test.ts | 2 + packages/dashboard/src/chat.ts | 2 + .../engine/src/__tests__/agent-action-gate.test.ts | 2 + .../src/__tests__/agent-tools-delegation.test.ts | 99 ++++++++++++++++++- .../src/__tests__/agent-tools-task-assign.test.ts | 75 +++++++++++++++ .../src/__tests__/gating-classifications.test.ts | 1 + .../src/__tests__/heartbeat-executor.test.ts | 8 +- .../src/__tests__/permanent-agent-gating.test.ts | 2 + .../src/__tests__/step-session-executor.test.ts | 4 +- packages/engine/src/__tests__/triage.test.ts | 5 +- packages/engine/src/agent-heartbeat.ts | 4 +- packages/engine/src/agent-tools.ts | 105 ++++++++++++++++++++- packages/engine/src/executor.ts | 3 + packages/engine/src/gating-classifications.ts | 1 + packages/engine/src/index.ts | 2 + packages/engine/src/step-session-executor.ts | 2 + packages/engine/src/triage.ts | 2 + 22 files changed, 324 insertions(+), 13 deletions(-) Fusion-Task-Id: FN-8207 Fusion-Task-Lineage: db6f3876-bdc8-4279-b726-29344d9acdb9 Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai> |
||
|
|
7a50232916 |
FN-8208: validate agent message recipients before delivery
Prevent false-success agent messages by validating recipients before delivery. - Wire AgentStore into every send-message tool registration. - Reject missing or unvalidated agent recipients before persistence or wake-up. - Add recipient validation coverage and a patch changeset. Files changed: .../fn-8208-send-message-recipient-validation.md | 7 ++ .../chat-send-message-agentstore-wiring.test.ts | 11 ++ packages/dashboard/src/chat.ts | 2 +- ...tools-send-message-recipient-validation.test.ts | 116 +++++++++++++++++++++ packages/engine/src/agent-heartbeat.ts | 4 +- packages/engine/src/agent-tools.ts | 24 ++++- packages/engine/src/executor.ts | 2 +- packages/engine/src/step-session-executor.ts | 2 +- 8 files changed, 162 insertions(+), 6 deletions(-) Fusion-Task-Id: FN-8208 Fusion-Task-Lineage: 84752bb8-c9f7-42bd-87c1-9681ac442789 Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai> |
||
|
|
28878d8125 |
FN-8206: validate landed merge checkout
Validate mission assertions against the delivered merge revision rather than an ambient working branch. - Materialize and dispose a detached checkout at mergeDetails.commitSha for validator sessions. - Defer failed validation when landed-code ancestry cannot be proven, preventing spurious fix features. - Link validator runs to their board tasks and document the inspection behavior. Files changed: .changeset/fn-8206-mission-validator-inspection-root.md | 7 + docs/missions.md | 2 + packages/engine/src/__tests__/mission-execution-loop.test.ts | 278 +++++++++++++-------- packages/engine/src/mission-execution-loop.ts | 160 ++++++++---- 4 files changed, 298 insertions(+), 149 deletions(-) Fusion-Task-Id: FN-8206 Fusion-Task-Lineage: dfd1eb21-fcdf-4722-acb8-416750f4f40b Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai> |
||
|
|
7fc4b439ee |
FN-8205: fix shared auth credential lock retries
Serialize shared auth credential writes and await durable persistence under contention. - Replace synchronous auth-file locking with queued asynchronous retries. - Propagate asynchronous credential mutations through CLI, dashboard, and provider wrappers. - Cover held-lock persistence, queue recovery, and awaited caller behavior. Files changed: .changeset/fn-8205-auth-lock-retry.md | 7 ++ .../cli/src/commands/__tests__/onboard.test.ts | 17 +++ .../src/commands/__tests__/provider-auth.test.ts | 78 ++++++------ packages/cli/src/commands/onboard.ts | 2 +- packages/dashboard/src/routes.ts | 6 +- .../dashboard/src/routes/register-auth-routes.ts | 10 +- .../dashboard/src/routes/register-mesh-routes.ts | 4 +- .../register-settings-sync-inbound-routes.ts | 4 +- .../src/routes/register-settings-sync-routes.ts | 4 +- .../src/__tests__/auth-storage-concurrency.test.ts | 84 ++++++++++--- packages/engine/src/__tests__/auth-storage.test.ts | 48 ++++---- packages/engine/src/auth-storage.ts | 137 +++++++++++++-------- packages/engine/src/provider-auth.ts | 56 ++++----- 13 files changed, 284 insertions(+), 173 deletions(-) Fusion-Task-Id: FN-8205 Fusion-Task-Lineage: b932b9ce-9aee-4f0a-9155-63b026c6bde4 Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai> |
||
|
|
e445b3e367 |
FN-8201: pin pi dependency versions
Pin the pi runtime packages to a single exact version so global npm installs resolve a compatible set. - Pin pi-ai and pi-coding-agent declarations across workspace manifests - Add a guard and tests that reject ranged or mismatched pi versions - Document the source-install fallback and add a patch changeset Files changed: .changeset/fn-8201-pin-pi-versions.md | 7 ++ docs/getting-started.md | 3 + package.json | 6 +- packages/cli/package.json | 4 +- packages/cli/src/__tests__/package-config.test.ts | 18 +++- packages/core/package.json | 2 +- packages/dashboard/package.json | 2 +- packages/engine/package.json | 4 +- packages/pi-claude-cli/package.json | 8 +- .../__tests__/check-pi-versions-pinned.test.mjs | 45 ++++++++ scripts/check-pi-versions-pinned.mjs | 120 +++++++++++++++++++++ 11 files changed, 205 insertions(+), 14 deletions(-) Fusion-Task-Id: FN-8201 Fusion-Task-Lineage: bf0ac363-df5f-4445-835b-cfd2d4909659 Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai> |
||
|
|
6ca7e48f87 |
fix(FN-7965): surface duplicate decisions
Show a clear operator-decision badge and deliver an idempotent mailbox prompt for triage duplicate markers. Fusion-Task-Id: FN-7965 |
||
|
|
7760d783bd |
fix: green full-suite after getAgentLogCount and inventory drift (#2266)
## Summary - Follow-up after #2229: full suite on main still failed on dashboard curated inventory (21 ungated files) and mass engine failures (`this.store.getAgentLogCount is not a function`). - Harden executor tool-failure cursor capture for minimal/test `TaskStore` adapters (same optional-API pattern as `project-engine`), keep mock fixtures in lockstep, and quarantine inventory-only dashboard files with ledger + vitest exclude. ## Changes - **Executor**: optional `getAgentLogCount` / `getAgentLogs` / `updateTask` at graph entry and trailing-failure detection. - **Mocks**: `createMockStore`, soft-delete guard, post-done continuation, cron `getGlobalSettingsDir`, executor-prompt `bulkCompletionRefusalAt` (FN-8141). - **i18n** (prior commit): es/fr/ko/zh-CN/zh-TW triage-duplicate keys. - **Inventory**: 21 dashboard files → `test-quarantine.json` + `vitest.config.ts` lockstep (VAL-REMOVAL SQLite / load flakes / build-only dist assert). ## Test plan - [x] `node scripts/check-test-inventory.mjs --dashboard-curated` - [x] `pnpm test:gate` - [x] engine: soft-delete, prompt, cron, post-done, tool-failure-retry, and related samples - [x] `@fusion/core` schema-applier + `@fusion/i18n` parity - [ ] Full Suite (non-blocking) on this PR / main after merge <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit - **New Features** - Added localized text for triage duplicate-resolution settings and near-duplicate task actions in Spanish, French, Korean, Simplified Chinese, and Traditional Chinese. - Users can now see translated options and confirmations to keep or delete detected duplicate tasks. - **Bug Fixes** - Improved resilience during task execution and recovery when optional activity-log services are unavailable, preventing avoidable failures during error handling. <!-- end of auto-generated comment: release notes by coderabbit.ai --> |
||
|
|
13bdf63bfc |
fix: resolve Anthropic subscription auth through getApiKey for pi ModelRuntime.getAuth
pi >=0.80.8 moved session request auth to ModelRuntime.getAuth -> pi-ai
resolveProviderAuth, which reads credentials.read("anthropic") and refreshes an
OAuth credential via credentials.modify("anthropic"). Fusion stores the
subscription login under `anthropic-subscription` with no raw `anthropic` row,
so the refresh callback saw current===undefined, bailed, and auth resolved to
undefined -> "Provider is not configured: anthropic" (then fell back).
Resolve read("anthropic") through fusion's getApiKey (refresh + raw/legacy/
subscription/fallback precedence) and hand pi-ai a ready api_key credential;
pi-ai routes it as OAuth by the sk-ant-oat token prefix. Supersedes the prior
read-alias, which fixed lookup but not the broken OAuth refresh-via-modify path.
Verified end-to-end: ModelRuntime.getAuth(anthropic/opus) now resolves the
subscription token instead of returning undefined.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
|
||
|
|
4b150e2280 |
fix: resolve Anthropic subscription auth under pi-ai >=0.80 read() contract
pi-ai >=0.80 resolves provider auth via credentials.read(provider.id) and
performs OAuth refresh/derivation itself, bypassing fusion's getApiKey()
where the anthropic-subscription -> anthropic alias lived. A subscription-only
login surfaced as "Provider is not configured: anthropic" at prompt time even
though the status card showed connected.
- Alias the subscription OAuth credential into read("anthropic") at the
credential-store layer (createFusionCredentialStore); raw/legacy rows still win.
- Match "not configured" in isRetryableModelSelectionError so an unresolved
provider triggers the configured fallback model instead of hard-failing.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
|
||
|
|
00cca460fc |
fix(engine): reclaim leaked semaphore slots when the system is busy, not only at total idle (#2265)
## fix(engine): reclaim leaked semaphore slots when the system is busy, not only at total idle ### The bug `recoverIdleSemaphoreLeakCandidate` only reclaims stale `AgentSemaphore` slots when the system is **completely** idle (`persistedActive === 0 && inFlightCount === 0` → reconcile to 0). If even one in-progress row persists — e.g. a zombie task whose agent session died without its `finally` release — the valve never opens, and slots leaked by abnormal teardown accumulate monotonically until `activeCount` pins the limit. At that point the engine deadlocks in a distinctive way: - every hold/release sweep logs `Hold release for <task> deferred — no reservable slot for in-progress` - triage/plan report `planning=0 … processing=0, semaphore active=<limit>/<limit>, available=0` - the merge queue grows unboundedly (merges also need a slot) - only a process restart recovers `reapLeakedConcurrencySlots` (FN-6782) doesn't help — it reconciles **worktree** slots, not the shared semaphore. ### Production evidence Observed twice on a 6-project embedded-PG deployment driving a local model: - After ~5 days of continuous operation: `semaphore active=24/24`, `planning=0/24, processing=0`, 5 persisted in-progress rows (dead sessions), merge queue at 88, **zero merges for >24h**. Restart immediately restored merging. - Same signature earlier at `active=40/40` with both LLM backends idle (`kvcache≈0`). The handful of zombie in-progress rows kept `persistedActive` nonzero indefinitely, so the idle-only valve could never fire. ### The fix Generalize the valve: clamp `activeCount` down to the **persisted + in-flight bound** whenever the semaphore over-holds **continuously** for a repair window. - The strict-idle case (`bound === 0`) keeps its existing fast 5s window — behavior unchanged, existing tests pass as-is. - The non-idle case uses a deliberately conservative new window (`STALE_SEMAPHORE_EXCESS_REPAIR_MS = 600_000`, 10 min): nested helper agents (`runNested`) legitimately push `activeCount` above the persisted top-level count for the duration of a nested run, so the excess must outlive any plausible nested session before it is treated as leaked. The candidate timestamp resets the moment the excess clears. - `reconcileActiveCount` only ever lowers the count, so the clamp cannot inflate capacity; a late release from a genuinely live agent after a (worst-case, mis-timed) clamp is absorbed by the existing excess-release guard (FN-6423). Call-site changes are limited to the two log messages (the old parenthetical claimed "no persisted … agent work", which is no longer the only repair case). ### Tests - existing idle-valve tests pass unchanged (same window, same reconcile-to-0) - new: stale excess above a nonzero persisted bound is repaired only after the long window, and clamps exactly to the bound - new: candidate resets when the excess clears (nested overshoot ending) - new: caller in-flight sessions count into the bound (no false candidate) ### Files - `packages/engine/src/concurrency.ts` — generalized valve + `STALE_SEMAPHORE_EXCESS_REPAIR_MS` - `packages/engine/src/scheduler.ts`, `packages/engine/src/triage.ts` — log message accuracy - `packages/engine/src/__tests__/concurrency.test.ts` — 3 new tests <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **Bug Fixes** * Improved stale “semaphore excess” recovery by using a configurable repair window when excess persists. * Prevented premature capacity corrections by accounting for in-flight top-level work during reconciliation. * Correctly handles nested helper activity so only leaked excess is reclaimed, preserving legitimate nested runs. * Updated reconciliation to clamp excess to the appropriate reclaim floor instead of waiting indefinitely. * **Improvements** * Refreshed diagnostic warning text to clarify the over-held vs persisted+in-flight work comparison. <!-- end of auto-generated comment: release notes by coderabbit.ai --> --------- Co-authored-by: TrinaryCompute <fusion-merge@trinarycompute.dev> Co-authored-by: gsxdsm <gsxdsm@users.noreply.github.com> Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com> |
||
|
|
daa34fbc38 |
fix: refineTask/duplicateTask fail in backend (PostgreSQL) mode (#2253)
## Summary Eliminates the remaining backend/PostgreSQL-mode sync-SQLite (`store.db`) call sites — both the crashing ones and the try/catch-masked ones that silently degraded features. Found via a full audit of `store.db`/`archiveDb` residue after the PG cutover's per-site routing missed them. **Crashes fixed:** 1. **refineTask / duplicateTask** threw `TaskStore.db: SQLite Database is not available in backend mode`. Both create rows through `createTaskWithId` callbacks calling `store.atomicCreateTaskJson()` directly, bypassing `_createTaskInternal`'s backend routing. The shared helper now routes itself (soft-delete conflict check + non-destructive insert in one AsyncDataLayer transaction). 2. **Merger verification cache**: `getVerificationCacheHit` ran sync SQLite unguarded *outside* any try/catch in `runDeterministicVerification`; `recordVerificationCachePass` was swallowed so the cache never warmed. Both are now async with a PG branch. **Silent degradations fixed (features that were dead on PG):** - Workflow run-branch + foreach step-instance persistence (`saveWorkflowRunBranch`, `loadWorkflowRunBranches`, `clearWorkflowRunBranches`, `saveWorkflowRunStepInstance`, `loadWorkflowRunStepInstances`, `clearWorkflowRunStepInstances`) — executor crash-resume checkpoints were silently never persisted. - `getBranchProgressByTask` — returned an empty map, dropping `branchProgress` from task payloads. - `runPluginColumnTransitionHooks` — plugin `onEnter`/`onExit` column-transition hooks never fired (marker bookkeeping + non-locking task read now async). - `getTaskColumns` — dashboard treated all agent-linked tasks as non-terminal. - `getWorkflowStep` / `listWorkflowSteps` — stored workflow-step rows now read from `project.workflow_steps` (listing previously returned plugin steps only); `getLegacyWorkflowStepSnapshot` returns `undefined` on PG (legacy snapshot exists only in pre-migration SQLite). - `readRawProjectSettings` / `listWorkflowPromptOverridesForProject` — now read via the async layer. These store methods became **async**; engine/dashboard callers await them (the workflow persistence interfaces already accepted `Promise`-returning impls). **PG gotcha encoded in the fixes:** migration `0006_project_ownership` rebuilds every project-schema PK to lead with `project_id`, so column-list `ON CONFLICT` inference fails (42P10) — upserts target the PK by constraint name. ## Surface Enumeration - Creators through `atomicCreateTaskJson`: `refineTaskImpl`, `duplicateTaskImpl` (fixed); `_createTaskInternalImpl` unaffected (already routed). - Verification-cache callers (all merger, all 3 sites now awaited). - Run-branch/step-instance callers: executor persistence adapters, parse-steps foreach probe, integration-queue flip, crash-resume reconcile, graph-reset cleanup; triage replan cleanup; dashboard spec-rebuild pin clears; agent-reflection rework summing — all awaited. - Audit classified everything else as guarded or sync-mode-only (dead in production — every entry point constructs stores via `createTaskStoreForBackend`). ## Symptom Verification - **Original symptoms:** refinement/duplicate creation threw; merge verification threw; workflow checkpoints/branch progress/plugin hooks/task-column lookups silently no-oped on PostgreSQL. - **Exact reproduction:** `refine-duplicate-task.pg.test.ts`, `verification-cache.pg.test.ts`, and `sync-db-residue-backend.pg.test.ts` exercise each surface against embedded-PostgreSQL backend-mode TaskStores. - **Assertion it is gone:** all suites pass (14 + 5 tests), plus `transition-pending-and-status-clear.pg.test.ts`, `create-task-reserved-id.pg.test.ts`, dashboard `routes-github.test.ts` (123), engine `triage.test.ts` (221) and `agent-reflection.test.ts` (31). Core/engine/dashboard typecheck fully clean: the 13 errors from the FN-8142 pi SDK migration are fixed by bumping @earendil-works/pi-ai/pi-coding-agent to ^0.80.10 (FN-8142 used APIs absent from the previously locked 0.80.6). Locally green: `pnpm verify:fast` (scoped typecheck + build + CLI build + boot smoke), `pnpm test:gate`, and `pnpm lint`. 🤖 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** * Fixed refinement/duplication task creation in PostgreSQL-backed backend mode. * Improved backend-mode persistence for workflow checkpoints, foreach-step instances, branch progress, and cleanup flows (including retries/resets/transitions), so stored data reliably round-trips. * Hardened backend-mode reads for workflow steps, task columns, project settings, and prompt overrides. * Made verification-cache reads/writes complete reliably, including command-specific cache behavior. * **Tests** * Added PostgreSQL integration/regression coverage for refinement/duplication, sync residue, and verification caching. * **Chores** * Bumped `@earendil-works/pi-ai` and `@earendil-works/pi-coding-agent` to `^0.80.10`. <!-- end of auto-generated comment: release notes by coderabbit.ai --> --------- Co-authored-by: Claude Fable 5 <noreply@anthropic.com> |
||
|
|
5f70447a62 |
FN-8108: require resolution for triage duplicate tasks
Require explicit resolution for triage-detected duplicate tasks. - Add configurable prompt, keep, and delete handling for explicit duplicate markers. - Surface linked duplicate decisions in task details and scheduling settings. - Preserve task failure provenance and strengthen lifecycle recovery coverage. Files changed: .changeset/blocked-park-survives-graph-teardown.md | 7 + .changeset/failure-provenance-promoter-marker.md | 7 + .changeset/fn-8108-triage-duplicate-resolution.md | 7 + .changeset/veto-progressing-does-not-clear.md | 7 + docs/settings-reference.md | 2 + .../completed-promotion-failure-provenance.test.ts | 31 +++ .../core/src/__tests__/duplicate-intake.test.ts | 11 ++ .../src/completed-promotion-failure-provenance.ts | 38 +++- packages/core/src/duplicate-intake.ts | 29 +++ packages/core/src/index.gate.ts | 1 + packages/core/src/index.ts | 3 +- packages/core/src/settings-schema.ts | 1 + packages/core/src/types.ts | 6 + .../dashboard/app/components/TaskDetailModal.tsx | 38 +++- .../__tests__/TaskDetailModal.rendering.test.tsx | 26 +++ .../settings/sections/SchedulingSection.search.ts | 9 + .../settings/sections/SchedulingSection.tsx | 15 ++ .../__tests__/routes-tasks-near-duplicate.test.ts | 31 +++ .../src/routes/register-task-workflow-routes.ts | 19 ++ .../__tests__/executor-task-done-blocked.test.ts | 212 ++++++++++++++++++++- packages/engine/src/__tests__/merger-ai.test.ts | 56 +++++- .../__tests__/overseer-noop-finalize-veto.test.ts | 64 ++++++- .../explicit-duplicate-marker-sweep.test.ts | 19 +- packages/engine/src/__tests__/self-healing.test.ts | 53 ++++++ .../triage-explicit-duplicate-marker.test.ts | 22 ++- packages/engine/src/executor.ts | 30 +++ packages/engine/src/merger-ai.ts | 5 +- packages/engine/src/overseer-noop-finalize-veto.ts | 148 ++++++++++---- packages/engine/src/self-healing.ts | 29 ++- packages/engine/src/triage.ts | 58 +++--- packages/i18n/locales/en/app.json | 15 +- packages/i18n/src/resources.d.ts | 13 +- 32 files changed, 897 insertions(+), 115 deletions(-) Fusion-Task-Id: FN-8108 Fusion-Task-Lineage: 8e732bad-d418-426e-85e1-903a7f990fba Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai> |
||
|
|
0753476c0a |
fix(engine): no-op finalize veto no longer cleared by mid-execution progressing — only accepted-completion evidence supersedes a failure park (#2263)
## What
FN-8141 follow-up 3. Tightens `deriveExecutorSignalMemory`
(packages/engine/src/overseer-noop-finalize-veto.ts) so a mid-execution
`progressing` overseer observation can no longer clear the executor
no-op-finalize veto.
## Why
The prior derivation took the **newest** executor `observe` entry and
cleared `incompleteWork` whenever it was anything but the canonical
failed reason. But the planner overseer emits a `progressing`
observation ("Task is actively executing in-progress work") the
**moment** a task re-enters execution — long before that execution
finishes. The defeating shape:
> task parks failed-incomplete → requeued → re-execution starts
(overseer observes `progressing`) → execution dies or reverts again
**without** a newer failed observation → newest observation is
`progressing` → `incompleteWork:false` → an empty no-op finalize is
**not** vetoed → the reverted branch launders into `done`.
`progressing` is not "completed green" — the veto's own contract says
the failure must be superseded by a green completion.
## Change
The executor stage in `planner-overseer.ts` emits only
`progressing`/`failed`/`stuck`/`blocked` — **no** green-completion
observation — so the timeline alone cannot distinguish progressing from
completed. Per the follow-up spec, the derivation now:
1. Scans the executor `observe` timeline for the newest
**failed-with-incomplete-work** observation.
2. Keeps `incompleteWork` TRUE unless a durable **clean-completion
task-log marker** is **strictly newer** than that failure park. Reuses
the shared `CLEAN_COMPLETION_MARKERS` set (now exported from
`@fusion/core`, single-sourced with
`evaluateCompletedPromotionFailureProvenance`) so it automatically
tracks sibling follow-up F2's removal of the promotion-output marker.
3. Fails safe on a malformed failure timestamp (stays vetoed).
4. `merger-ai.ts` threads `task.log` into the derivation.
All existing precedence rules are preserved: non-empty merges are never
vetoed; human-control deferral (user-paused / autoMerge:false) still
defers; a missing task fails open.
## Test evidence
- `pnpm --filter @fusion/engine exec vitest run
src/__tests__/overseer-noop-finalize-veto.test.ts
src/__tests__/merger-ai.test.ts` → **59 passed**. New/updated cases:
failed→progressing (no completion) ⇒ still vetoed (the regression this
fixes); progressing between two failed parks (FN-8141 timeline) ⇒
vetoed; clean-completion marker newer than failure ⇒ not vetoed; older
completion marker ⇒ still vetoed; no failure park ⇒ not vetoed; bounded
tail-scan preserved. Integration: empty lane with
failed-then-progressing timeline blocks the finalize; genuinely
re-executed green task finalizes to done.
- `pnpm --filter @fusion/core exec vitest run
src/__tests__/completed-promotion-failure-provenance.test.ts` → 9
passed.
- `pnpm --filter @fusion/engine exec tsc --noEmit` → clean. `pnpm
--filter @fusion/core exec tsc --noEmit` → clean.
- `pnpm verify:fast` → PASS (3 steps green).
🤖 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 empty-merge finalization safeguards so an in-progress task
cannot incorrectly clear a previously detected incomplete-work failure.
* Finalization can now proceed when a newer clean-completion record
confirms successful completion.
* Added bounded task-history evaluation to ensure completion records are
interpreted safely and consistently.
* **Tests**
* Expanded coverage for progressing, failed, and clean-completion task
timelines.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
Co-authored-by: Claude Opus <noreply@anthropic.com>
|
||
|
|
46866a5c5a |
fix(core): promoter recovery output no longer counts as clean-completion evidence in the failure-provenance guard (#2262)
## What FN-8141 follow-up 2. Removes `"Auto-recovered: task work was complete but stranded"` from `CLEAN_COMPLETION_MARKERS` in `packages/core/src/completed-promotion-failure-provenance.ts`. Clean-completion evidence is now **execution outcomes only**: `"Task marked done by agent"` (accepted explicit fn_task_done, also covers the PREMISE STALE skip-then-done flow) and `"All steps complete — implicit fn_task_done"` (implicit-completion success). ## Why That string is the PROMOTER'S OWN OUTPUT — self-healing's `recoverCompletedTasks` (executor.ts:4594) narrating "I promoted this task" — not evidence of a clean execution outcome. Any task whose durable log contains a promotion written by the pre-#2257 buggy sweep (the real FN-8141 row, or any pre-guard history) carried a permanent "clean" marker: the tail scan hit the promotion line before the older failure park and returned not-blocked, re-enabling the exact laundering the guard exists to stop. Audit confirmed no other genuine execution-outcome success markers are missing — the PREMISE STALE accepted `fn_task_done` writes the already-listed `"Task marked done by agent"` line (executor.ts:14939), and the honest-blocked exit (`BLOCKED: ...`) is correctly NOT counted. `grep` confirmed the removed string has only one other consumer: its writer at executor.ts:4594. A task already promoted to in-review/done is out of the promoters' todo/in-progress scan scope, so legitimately-recovered old tasks are not wedged (verified by test rather than assumed). ## Test evidence - Core `completed-promotion-failure-provenance.test.ts`: **11 passed** — added pre-fix-history shape (failure park → promoter recovery line → blocked), promoter-line-alone → blocked, and positive coverage of each remaining marker. - Engine `self-healing.test.ts`: **405 passed** — added promoter withholds on the pre-fix-history shape and emits the existing `task:reconcile-stranded-completed-no-action` (reason `failure-provenance`) event. - `pnpm --filter @fusion/engine exec tsc --noEmit`: clean. - `pnpm verify:fast`: PASS (3 steps green, no tests run). 🤖 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** - Prevented failed tasks with prior failure history from being automatically promoted. - Ensured recovery messages cannot override authoritative failure records or be mistaken for successful completion. - Preserved the existing no-action behavior and audit event when promotion is blocked by failure provenance. - **Tests** - Added regression coverage for failed-task promotion and stranded-task recovery scenarios. <!-- end of auto-generated comment: release notes by coderabbit.ai --> Co-authored-by: Claude Opus <noreply@anthropic.com> |
||
|
|
f116d05c41 |
fix(engine): honest BLOCKED park survives pause-abort and workflow-graph teardown (#2264)
## What Follow-up 1 to the FN-8141 guard series (#2254–#2260). Makes the honest `fn_task_done(outcome="blocked")` park (`status:"failed"`, `error:"BLOCKED: <reason>"`, blockedBy → dependencies, added in #2256) **survive the graph-teardown machinery** that bounced FN-8141's failed park back to `todo`. ## Why In the original FN-8141 incident, the executor's parked-failed state did not stick: the pause-abort classifier and the workflow-graph failure handler either rehomed the task to `todo` (clearing `status`/`error`) or overwrote the distinctive `BLOCKED:` error with a generic "Workflow graph terminated with failure" string. #2256 added the blocked exit but nobody proved the park survives that bounce. Any path that clears/overwrites the marker re-opens the laundering hole, because self-healing (#2257/#2260) and dependency-gated scheduling key off exactly that `BLOCKED:` error plus the recorded `blockedBy` dependencies. `handleGraphFailure` now detects a live blocked park (`status === "failed" && error.startsWith("BLOCKED:")`) **before every other classifier** and honors it, following the existing non-graph honor-park precedent (executor `~12163`): - no requeue to `todo`, no engine-internal auto-continue, no `BLOCKED:` error overwrite; - clears the in-memory pause-abort marker so `recoverPausedAbortFailures` has nothing to chase; - **releases the worktree / `maxWorktrees` slot** (FN-6782 leaked-holder precedent — the graph `finally` does not delete `activeWorktrees`); - leaves `status`/`error`/`column`/`dependencies`/steps untouched. Unblocking still works: the operator requeue (`moveTask` in-progress→todo, `moves.ts ~628`) and `buildManualRetryResetPatch` clear the `BLOCKED:` error; the guard keys off the **live** error, so a cleared row is never re-wedged, and dependency-gated scheduling leaves the parked row untouched while `blockedBy` deps are unmet. ## Surfaces covered Pause-abort classifier (hard-cancel), engine-internal auto-continue, and the plain terminal graph-failure sink — all routed through `handleGraphFailure`, so a single top-of-method guard composes across them. ## Test evidence Extended `executor-task-done-blocked.test.ts` (drives `handleGraphFailure` against a live blocked park): - honors the park under a hard-cancel pause-abort bounce (no requeue / clear / auto-continue); - honors it under a plain terminal graph failure (sink never overwrites `BLOCKED:`); - releases the worktree/concurrency slot + clears the pause-abort marker; - NON-blocked failed park keeps existing behavior (guard scoped to `BLOCKED:`); - a cleared (unblocked) row is NOT re-honor-parked. ``` pnpm --filter @fusion/engine exec vitest run src/__tests__/executor-task-done-blocked.test.ts → 13 passed pnpm --filter @fusion/engine exec vitest run executor-paused-abort-todo-benign + executor-graph-requeue-gate → 53 passed pnpm --filter @fusion/engine exec tsc --noEmit → clean pnpm verify:fast → PASS (3 steps green) ``` 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-authored-by: Claude Opus <noreply@anthropic.com> |
||
|
|
e72629c251 |
FN-8126: add per-task merger model controls
Enable Quick Add and task editing to select merger models and thinking levels. - Persist merger model and thinking overrides through task APIs, storage, and PostgreSQL migrations. - Add merger-lane selection controls to Quick Add and model settings interfaces. - Apply task merger settings to merger and PR fallback sessions, with regression coverage. - Document the merger lane and include a release changeset. Files changed: .changeset/fn-8126-quick-add-merger-lane.md | 7 ++ docs/dashboard-guide.md | 2 + docs/settings-reference.md | 7 +- .../core/src/__tests__/model-resolution.test.ts | 9 ++ packages/core/src/index.gate.ts | 1 + packages/core/src/index.ts | 1 + packages/core/src/model-resolution.ts | 20 ++++ .../core/src/postgres/migrations/0000_initial.sql | 3 + .../migrations/0017_task_merger_model_lane.sql | 4 + packages/core/src/postgres/schema-applier.ts | 14 ++- packages/core/src/postgres/schema/project.ts | 3 + packages/core/src/store.ts | 2 +- .../core/src/task-store/archive-lifecycle-2.ts | 6 ++ packages/core/src/task-store/persistence.ts | 6 ++ packages/core/src/task-store/remaining-ops-2.ts | 4 +- packages/core/src/task-store/remaining-ops-6.ts | 2 +- packages/core/src/task-store/serialization.ts | 6 ++ packages/core/src/task-store/task-creation.ts | 6 ++ packages/core/src/task-store/task-row-mappers.ts | 4 +- packages/core/src/task-store/task-update.ts | 6 ++ packages/core/src/types.ts | 18 ++++ packages/dashboard/app/api/tasks.ts | 13 +++ .../dashboard/app/components/InlineCreateCard.tsx | 33 ++++++- .../app/components/ModelSelectionModal.tsx | 29 ++++++ .../dashboard/app/components/ModelSelectorTab.tsx | 101 +++++++++++++++++++-- .../dashboard/app/components/QuickEntryBox.tsx | 44 +++++++-- .../__tests__/ModelSelectionModal.test.tsx | 20 ++++ .../components/__tests__/ModelSelectorTab.test.tsx | 37 +++++++- .../src/routes/register-task-workflow-routes.ts | 27 +++++- .../src/__tests__/agent-session-helpers.test.ts | 8 ++ packages/engine/src/agent-session-helpers.ts | 16 +++- packages/engine/src/merger-ai.ts | 14 +-- packages/engine/src/merger.ts | 35 ++++--- packages/engine/src/pr-response-run-ops.ts | 7 +- 34 files changed, 451 insertions(+), 64 deletions(-) Fusion-Task-Id: FN-8126 Fusion-Task-Lineage: 3fc81801-6d77-4e11-9cf0-3af37313930e Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai> |
||
|
|
b747f51f8b |
FN-8185: use async persistence for reflection rework reads
Resolve workflow rework metrics from the persisted production run. - Resolve workflow selections to definition-backed run IDs - Prefer asynchronous step-instance reads with legacy-store compatibility - Cover PostgreSQL, fallback, and degraded reflection metrics Files changed: .../engine/src/__tests__/agent-reflection.test.ts | 51 +++++++++++++++++++--- packages/engine/src/agent-reflection.ts | 50 ++++++++++++++------- 2 files changed, 80 insertions(+), 21 deletions(-) Fusion-Task-Id: FN-8185 Fusion-Task-Lineage: 2554d5f1-62b7-47a6-bad0-c00a7d649a2d Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai> |
||
|
|
a136535f15 |
fix(engine): taint steps skipped after a bulk-completion refusal so they cannot auto-promote (#2260)
## What & why
**FN-8141 laundered a failed task into `done` with zero net changes and
no sign-off.** After the executor's
`bulk-step-completion-without-review` refusal fired (steps had no
APPROVE verdicts), the agent used the sanctioned skip affordance
(`fn_task_update status="skipped"`) on the remaining unreviewed steps.
Because every completion check counts `skipped` as complete, the task
then satisfied the exact condition the refusal was protecting, and
downstream **automatic** promotion (implicit `fn_task_done`,
self-healing `recoverStrandedCompletedTodoTasks`) moved it to in-review
— where the AI merger found an empty diff and finalized it as a no-op
`done`.
This PR restores the invariant: **steps skipped while a
bulk-step-completion refusal marker is active on the task are "tainted"
and cannot carry the task to review through any automatic path.** The
taint clears on an honest exit — an accepted `fn_task_done` (explicit or
non-tainted implicit) or an operator manual retry — so the legitimate
`PREMISE STALE` skip-then-done flow is unaffected.
## Design
- **Persisted marker**: new nullable `Task.bulkCompletionRefusalAt` (ISO
timestamp), stamped when the `bulk-step-completion-without-review`
refusal fires (explicit `fn_task_done` handler + implicit
`handleImplicitTaskDoneRefusal`). Survives requeue so a refusal on
attempt N taints attempt N+1's promotion. Full store plumbing (types,
descriptors, serialization, SQLite/PG schema + health self-heal).
- **Pure evaluator** `evaluateSkipBypassTaint(task)` in `@fusion/core`
(next to `evaluateNoCommitsNoOpFinalize`): `blocked` iff the marker is
set AND ≥1 step is `skipped`. Single rule every AUTO-promotion check
calls.
- **Clearing**: accepted explicit `fn_task_done`, accepted
implicit/retry completion (the success-reset `updateTask`s), and
`buildManualRetryResetPatch` (operator retry). A fresh lifecycle that
genuinely re-does the work leaves zero skipped steps, so it is never
blocked even if a marker lingers.
## Surface enumeration (every consumer of "all steps done/skipped" that
gates AUTO-promotion)
- **executor.ts**: `getCompletedTaskFinalizationDecision` (gated on the
`isTaskWorkComplete` branch only, never on an accepted `taskDone`);
`recoverCompletedTask` (shared chokepoint for unpause resume,
completed-task watchdog, orphan resume);
`evaluateImplicitCompletionRefusal` (both implicit-completion loops);
`isTaskAlreadyCompleteForNonContinuableSession`; graph merge-boundary
`getWorkflowMergeImplementationProofFailure`.
- **self-healing.ts**: `recoverCompletedTasks` (stuck in-progress) and
`recoverStrandedCompletedTodoTasks` (the exact FN-8141 promoter).
- **Verified-safe, left as-is**: per-step graph node projections
(executor ~6274/6298) and progress-render checks — they don't gate
whole-task auto-promotion.
## Test evidence
Scoped runs (all green):
```
CORE: pnpm --filter @fusion/core exec vitest run \
src/__tests__/skip-bypass-taint-guard.test.ts \
src/__tests__/skip-bypass-taint-persistence.test.ts \
src/__tests__/manual-retry-reset.test.ts
→ 17 passed
ENGINE: pnpm --filter @fusion/engine exec vitest run \
src/__tests__/executor-skip-bypass-taint.test.ts \
src/__tests__/self-healing.test.ts
→ 401 passed
```
Coverage: pure-evaluator (skip-before-refusal counts, skip-after-refusal
doesn't, taint-clearing, empty-marker/empty-steps edges); store
round-trip of the marker (set→read→clear); executor white-box (implicit
completion refused when tainted, allowed when clean or fully re-done,
graph merge-boundary reports missing proof, and the **explicit
`fn_task_done` PREMISE-STALE honest exit stays accepted**); self-healing
(FN-8141 sequence does not promote from either recovery path; a clean
legitimately-skipped task still promotes); manual-retry clears the
marker.
## Note on `pnpm verify:fast`
`verify:fast` currently fails at the workspace-artifact bootstrap on
**pre-existing** pi-SDK type errors in
`packages/engine/src/{auth-storage,pi,provider-registration}.ts` — the
FN-8145 upstream migration breakage (pi 0.80.x removed
`AuthStorage`/`ModelRegistry.create`). **None of those files are in this
diff.** `@fusion/core` builds clean (`packages/core build: Done`), and
`@fusion/engine` `tsc` reports **no errors in the files this PR
touches** (`executor.ts`, `self-healing.ts`); the only engine build
errors are the FN-8145 files. This base failure is the same condition
FN-8141 describes and is out of scope for this task.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-authored-by: Claude Opus <noreply@anthropic.com>
|
||
|
|
29543a0aac |
FN-8157: add PostgreSQL workflow step-instance persistence
Persist workflow foreach step-instance state through async PostgreSQL store APIs. - Add async save, load, and stale-run pruning operations backed by Drizzle. - Route executor persistence, recovery, and integration projection through async APIs. - Cover PostgreSQL persistence and migrate foreach wiring coverage to the PG harness. - Quarantine unrelated flaky route and triage tests per the test ledger. Files changed: .../workflow-run-step-instances.pg.test.ts | 100 +++++++++++++++++++ packages/core/src/store.ts | 14 ++- packages/core/src/task-store/remaining-ops-6.ts | 109 ++++++++++++++++++++- .../dashboard/src/__tests__/routes-github.test.ts | 14 +-- .../src/routes/register-task-workflow-routes.ts | 18 ++-- packages/engine/src/__tests__/triage.test.ts | 6 +- .../src/__tests__/workflow-foreach-wiring.test.ts | 59 +++++------ packages/engine/src/executor.ts | 57 ++++++++--- packages/engine/src/triage.ts | 4 +- packages/engine/vitest.config.ts | 2 +- scripts/lib/test-quarantine.json | 7 +- 11 files changed, 315 insertions(+), 75 deletions(-) Fusion-Task-Id: FN-8157 Fusion-Task-Lineage: c359f0d3-9191-4d27-aaed-9912419c5c27 Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai> |
||
|
|
aa1e250dd3 |
fix(engine): overseer vetoes no-op merge finalize when the last executor signal was failed-with-incomplete-work (#2258)
## What & why FN-8141 (\"Update pi SDK … verify Kimi K3 end to end\") was **laundered into `done`** despite producing zero net changes. The executor reverted the impossible work 5 times; the planner overseer emitted `stage=executor signal=failed` (\"Executor stage parked failed with work incomplete\") **twice**, then an hour later — because the overseer is **stage-scoped and memoryless** — classified the same task `stage=merger signal=progressing` and let the AI merger's **empty no-op finalize** promote it to `done`. No reviewer ever saw it (skipped steps request no review; the merge-review pass reviews an empty diff). This restores the invariant: **a task whose most-recent executor signal is failed-with-incomplete-work, with no subsequent green completion, must not reach `done` via a zero-diff no-op merge finalize.** ## Change Two pure, unit-testable, never-throw functions (`packages/engine/src/overseer-noop-finalize-veto.ts`), following the FN-7514 `evaluateOverseerHumanControl` precedent (pure predicate + ids/outcomes-only audit metadata): - **`deriveExecutorSignalMemory`** — reconstructs the most-recent executor signal from the **durable `overseer:intervention` timeline the overseer already writes** (no new persisted column / migration; \"the existing oversight state storage the controller uses\"). A later green executor observation supersedes an earlier failure, which is how \"no subsequent execution completed green\" is derived. Keys on the newly-exported `EXECUTOR_FAILED_INCOMPLETE_REASON` constant (already load-bearing for FN-7577 feed dedup) as the single source of truth. - **`evaluateNoOpFinalizeExecutorVeto`** — the veto decision. Wired into the AI **empty-merge lane** (`merger-ai.ts`), composed with (and independent of) the FN-6461 no-commits guard: on veto it sets `error`, writes a durable log entry, emits `overseer:no-op-finalize-vetoed-failed-executor`, and moves the task back to `todo` with progress preserved — mirroring the FN-6461 blocked lane. The move-to-todo transition takes the task out of the merge lane, so the event isn't re-emitted every poll (equivalent to the `overseer:oversight-withheld-human-control` per-(taskId, reason) dedup). Independent of the sibling Task 2 merger-layer lineage guard: both can fire; **either alone stops FN-8141**. ## Surface enumeration - **Empty (zero-diff) no-op merge** — vetoed when memory is failed-incomplete. ✅ - **Non-empty (real squash landed) merge** — **never** vetoed (reviewers/merge review cover real diffs). ✅ - **failed-incomplete then later green execution** — memory superseded → no veto. ✅ - **No executor memory / store lacks the async reader** — fail open (no veto); FN-6461 + sibling guard remain the safety net. ✅ - **user-paused / autoMerge:false / approval-blocked** — defers to FN-7514 human-control; never fights those semantics. ✅ - **Timeline entry filtering** — only `stage=executor` + `action=observe` entries count as signals (retry/escalate messages ignored). ✅ ## Test evidence `pnpm --filter @fusion/engine exec vitest run src/__tests__/overseer-noop-finalize-veto.test.ts src/__tests__/merger-ai.test.ts --silent=passed-only --reporter=dot` ``` Test Files 2 passed (2) Tests 48 passed (48) ``` Covers: derivation (failed→veto, failed-then-green→no-veto, non-executor/non-observe ignored, empty→null); evaluator (veto, green, no-memory, non-empty never-vetoed, user-paused defer, autoMerge:false defer, missing-task fail-open); and an engine integration test driving an **FN-8141-shaped** empty merge through `runAiMerge` → asserts move-to-todo + `overseer:no-op-finalize-vetoed-failed-executor` audit event + main untouched, plus the later-green case finalizing done. `@fusion/core` builds clean (`pnpm --filter @fusion/core build`). **Note on `pnpm verify:fast`:** it currently fails to build `@fusion/engine`, but **only** in `auth-storage.ts` / `pi.ts` / `provider-registration.ts` — the pre-existing pi-SDK breakage that *is* this incident (pi 0.80.x removed `AuthStorage`/`ModelRuntime`/`ModelRegistry`; tracked as FN-8145). Verified identical errors with my changes stashed; **my diff touches none of those files and adds zero new type errors** (tsc reports all program errors before failing — none were in my files). 🤖 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** - Prevented zero-change tasks from being incorrectly finalized when the latest executor attempt failed with unfinished work. - Preserved task progress and returned affected tasks to **Todo** for continued processing. - Allowed finalization to proceed after a subsequent successful executor result. - Maintained existing human-control and non-empty merge behavior. - Added audit visibility for blocked finalization events. <!-- end of auto-generated comment: release notes by coderabbit.ai --> Co-authored-by: Claude Opus <noreply@anthropic.com> |
||
|
|
f079245acc |
fix(engine): defensive getAuth guard on session routing seam + realign pi tests to ModelRuntime seam (#2261)
## What / Why **Reworked after FN-8179 (`fd43a57a4`) landed on `main`.** FN-8179 did most of what the original PR #2261 did — pinned `@earendil-works/pi-ai` / `pi-coding-agent` to `^0.80.10` everywhere, added the `createSessionOptions` `NonNullable` typing in `pi.ts`, and regenerated `pnpm-lock.yaml`. This PR was rebased onto current `origin/main` and reduced to **only the unique residual not covered by FN-8179**. ### Residual change 1 — defensive `getAuth` guard The FN-8142 migration rewrote `attachSessionRoutingHeaders` from the `ModelRegistry.getApiKeyAndHeaders` seam to `ModelRuntime.getAuth`, but dropped the pre-migration defensive invariant: a missing resolution method must **not** break session creation. On `main` the function now calls `modelRuntime.getAuth.bind(...)` unguarded, which throws if `getAuth` is ever absent. This restores the guard: no-op (warn) when `getAuth` is missing, so a future pi rename degrades to un-tagged requests instead of a hard failure at every agent start. ### Residual change 2 — test realignment (needed against current main) FN-8179 aligned the SDK but did **not** update the two `#1675` routing-header test suites, which still asserted the old `getApiKeyAndHeaders` seam. **Verified RED on current `main` before touching them:** - `pi-create-fn-agent.test.ts` — **60 / 104 failing** (mock had no `ModelRuntime` export; `createAgentSession` now receives `modelRuntime`). - `pi-session-routing-headers.test.ts` — **4 / 5 failing** (`attachSessionRoutingHeaders` signature is `getAuth`, not `getApiKeyAndHeaders`). Both are realigned to the `ModelRuntime.getAuth` seam (the mock gains the `ModelRuntime` export) → **109 / 109 green**. Assertions were strengthened to the new behavior, not weakened; the #1675 precedence invariant (taskId > pi session id > no-wrap), header merge, apiKey/provider-header passthrough, failed/undefined passthrough, and absent-method no-op are all still asserted. ## Surface enumeration - **Routing-header seam**: both the `createFnAgent` path and the `attachSessionRoutingHeaders` unit (taskId / pi-session-id / no-id precedence; header merge; apiKey + provider-header passthrough; failed/undefined resolution; absent-method no-op). - **Both mock forms** in `pi-create-fn-agent.test.ts`: the top-level `vi.mock` and all three `vi.doMock` skill-selection blocks now export `ModelRuntime`. ## Test evidence - `pnpm --filter @fusion/engine exec tsc --noEmit` → **exit 0**. - `pnpm --filter @fusion/engine exec vitest run` on both suites → **109/109 pass** (was 64 failing on main). - `pnpm verify:fast` → **PASS** (typecheck + build scoped to changed packages + CLI build + boot smoke `GET /api/health 200`; no tests run). Do not merge without CI. No release performed. 🤖 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 session creation reliability when authentication support is unavailable. * Preserved existing authentication details while adding session-routing headers when supported. * Prevented session creation from failing when authentication information cannot be resolved. <!-- end of auto-generated comment: release notes by coderabbit.ai --> Co-authored-by: Claude Opus <noreply@anthropic.com> |
||
|
|
ced0e84f5d |
FN-8184: unify heartbeat multiplier timing
Keep scheduler repair and reports health aligned with the effective heartbeat cadence. - Apply heartbeatMultiplier once through shared interval calculations. - Align scheduler repair and reports-health stale thresholds with scaled cadence. - Add multiplier regression coverage and document the effective timing rules. Files changed: .../fn-8184-heartbeat-multiplier-consistency.md | 7 ++ docs/agents.md | 8 +- .../src/__tests__/heartbeat-executor.test.ts | 43 +++++++++ .../src/__tests__/heartbeat-scheduler.test.ts | 46 ++++++++++ packages/engine/src/agent-heartbeat.ts | 101 ++++++++++++++++----- 5 files changed, 176 insertions(+), 29 deletions(-) Fusion-Task-Id: FN-8184 Fusion-Task-Lineage: 05b79c5e-444a-42c3-9073-9c8c05df5def Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai> |
||
|
|
26cb0cc2e1 |
FN-8180: add Kimi K3 catalog and pricing support
Expose Kimi K3 throughout the native pi catalog and token-cost surfaces. - Add K3 pricing and normalized lookup coverage for Dashboard cost calculations. - Verify native K3 registry, API model route, and repeat-stop behavior end to end. - Add a minor release changeset for Kimi K3 model selection support. Files changed: .changeset/fn-8180-pi-sdk-kimi-k3.md | 7 +++ packages/core/src/__tests__/model-pricing.test.ts | 7 ++- packages/core/src/model-pricing.ts | 17 +++++- ...ister-model-routes-kimi-k3-supplemental.test.ts | 70 ++++++++++++++++++++++ .../pi-prompt-session-and-check-recursion.test.ts | 15 +++++ .../src/__tests__/provider-registration.test.ts | 17 ++++++ 6 files changed, 130 insertions(+), 3 deletions(-) Fusion-Task-Id: FN-8180 Fusion-Task-Lineage: 07598d73-c866-4329-b5cf-3aebc1c0a180 Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai> |
||
|
|
136958fc60 |
fix(engine): stranded-completed promoters withhold tasks whose last execution ended in a failure park (#2257)
## What & why
FN-8141 laundered a failed task into `done`. The executor correctly
parked the task `failed` ("task parked failed during no-fn_task_done
retry" / "fn_task_done refusal retry budget exhausted"), the pause-abort
machinery bounced it to `todo`, and ~12 minutes later
`recoverStrandedCompletedTodoTasks` promoted it to `in-review` because
every step was done/skipped — overriding the honest failure park. From
there the AI merger found an empty diff and finalized it as a no-op
`done`, with no reviewer ever seeing it.
Existing exclusions (`task.error`,
`evaluateNoCommitsNoOpFinalize().blocked`, active statuses, refreshing
review state) all missed it because the failure provenance lived **only
in the durable task log** by the time the promoter ran — status/error
had been cleared by the pause-abort bounce.
This PR restores the invariant: **a stranded-completed promoter must not
promote a task whose most recent execution lifecycle ended in a
failure/refusal park.**
## Change
- New pure, unit-testable evaluator
`evaluateCompletedPromotionFailureProvenance(task)` in `@fusion/core`
(next to `no-commits-finalize-guard.ts`). It scans the task-log **tail**
(bounded to 250 entries) and lets the **most-recent execution-outcome
marker** decide: a failure/refusal park → `{ blocked: true, reason:
"failure-provenance" }`; a fresh clean completion (`Task marked done by
agent` / `All steps complete — implicit fn_task_done`) that appears more
recently supersedes an earlier park; zero failure markers → not blocked.
Recency is by construction, so a failure that predates a newer clean
execution is never reached.
- Both self-healing sweeps (`recoverCompletedTasks` stuck-in-progress
**and** `recoverStrandedCompletedTodoTasks` stranded-todo) fetch the
full task for candidates that already cleared the cheap slim filters
(slim listings strip `log`) and skip when blocked, emitting a
**deduped** `task:reconcile-stranded-completed-no-action` run-audit
event (ids/outcomes-only: `taskId`, `reason`, `sweep`, `marker?`).
- Defense-in-depth: the shared executor `recoverCompletedTask`
chokepoint — which the sweeps AND the executor's own
unpause/`resumeOrphaned` fast-paths all funnel through — also refuses a
provenance-blocked promotion, so no route can launder a failed park.
**Escape hatch (documented in FNXC comments):** an operator
retrying/moving the task starts a fresh execution whose clean-completion
marker supersedes the failure park, clearing the block with no code
change.
## Surface enumeration
- `recoverCompletedTasks` (stuck-in-progress sweep, self-healing.ts) —
guarded + audited.
- `recoverStrandedCompletedTodoTasks` (stranded-todo sweep,
self-healing.ts) — guarded + audited. FN-8141 shows both columns can
launder.
- `recoverCompletedTask` executor callback (the route both sweeps +
unpause + `resumeOrphaned` share) — verified it did **not** check
log-based provenance; added the guard there as the final chokepoint.
## Test evidence
Pure-evaluator unit tests (`@fusion/core`) — marker detection,
most-recent-outcome recency, supersede-by-clean-completion,
empty/missing log, tail-scan bound:
```
pnpm --filter @fusion/core exec vitest run src/__tests__/completed-promotion-failure-provenance.test.ts
Test Files 1 passed (1) Tests 9 passed (9)
```
Self-healing integration tests (`@fusion/engine`) — FN-8141-shaped todo
(3 done + 2 skipped + refusal-exhaust/park marker) is NOT promoted and
emits the no-action event exactly once (deduped across a second cycle);
same task after a fresh clean execution IS promoted; stuck-in-progress
variant covered:
```
pnpm --filter @fusion/engine exec vitest run src/__tests__/self-healing.test.ts -t "recoverCompletedTasks|recoverStrandedCompletedTodoTasks|FN-8141"
Test Files 1 passed (1) Tests 14 passed | 382 skipped (396)
```
`@fusion/core` builds clean. My engine changes add **zero** new type
errors (verified: all 13 engine build errors are the pre-existing pi-SDK
cluster in `auth-storage.ts`/`pi.ts`/`provider-registration.ts`, none in
`self-healing.ts`/`run-audit.ts`/`executor.ts`/the new file).
## Known environmental blocker
`pnpm verify:fast` cannot go green on this branch: the `@fusion/engine`
build is **already broken at baseline** (confirmed by stashing all my
changes) by the pi 0.80.x SDK migration errors
(`ModelRegistry`/`AuthStorage`/`ModelRuntime`) — the exact FN-8145
upstream breakage described in the FN-8141 incident. That is out of
scope for this task and independent of this diff. Likewise, the 22
pre-existing
`restart.integration.test.ts`/`executor-fast-mode-workflows.test.ts`
failures are identical with and without my changes.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-authored-by: Claude Opus <noreply@anthropic.com>
|
||
|
|
1c4fdb7b59 |
fix(engine): require already-landed proof before finalizing an empty AI merge as done (#2259)
## What & why Task FN-8141 laundered a failed task into `done`: its branch had no net changes vs `main` **only because the executor reverted its own work five times**, and the AI empty-merge lane took the "empty means already-landed or nothing-to-do → finalize as no-op done" path, stamping `mergeConfirmed: true` with no reviewer or operator sign-off. This restores the invariant: **a commit-expected task that reaches the empty AI-merge outcome must not finalize `done` without positive evidence the work already landed.** `packages/engine/src/merger-ai.ts` empty-outcome lane now, for a commit-expected task (`noCommitsExpected !== true`), requires one of: 1. Durable recorded landing on the task (`mergeDetails.mergeConfirmed`/`commitSha`). 2. A prior AI no-op finalization proof pair in the task log (`hasPriorAiNoOpFinalizationProof`, FN-7261 shape). 3. The branch tip is an **ancestor of the integration branch** (fast-forwarded / zero-ahead / already-integrated). 4. The already-on-main classifier (`detectAlreadyLandedOnMain`) finds a distinct landing commit via a **strong** strategy (`trailer`/`ancestry`/`patch-id`). The classifier's weak `tree-equal`/`no-diff` strategies are **deliberately rejected**: a branch that committed work then reverted it back to base has a tree equal to `main` (main never advanced), which is exactly the FN-8141 shape and would false-positive. Absent proof, the task gets `task.error` set, emits run-audit `task:empty-merge-finalize-blocked-no-landed-proof` (ids/counts/outcomes-only), and is moved back to `todo` with progress preserved — mirroring the existing FN-6461 blocked lane. `noCommitsExpected === true` tasks are untouched (hardened separately in the sibling Task 1). The non-empty landed path, group/PR routing, and push-after-merge behavior are unchanged. ## Surface enumeration - **Single-repo empty-outcome finalize (primary lane)** — guarded in `runAiMerge`. - **Workspace/multi-repo caller** — `landWorkspaceTask`'s all-empty finalize is a second route. Already-landed sub-repos are proven up front by `findProvenLandedCommit` and marked `status:"landed"`; when `landedCount === 0` the guard re-checks each empty sub-repo's branch and blocks the FN-8141 reverted shape (tip not an ancestor / branch vanished) identically. (Note: the genuinely-integrated all-empty workspace case already throws `missing-merge-confirmation` on `mergeConfirmed:false`, so it never reached `done`; that pre-existing path is left intact.) - **Re-promotion ping-pong** — the blocked path sets `task.error`, and `recoverStrandedCompletedTodoTasks` excludes any task with `task.error`, so the promoter cannot re-promote the unchanged blocked task. Regression-tested. ## Test evidence Scoped tests (all green): ``` vitest run merger-ai.test.ts workspace-merger.test.ts → 46 passed vitest run self-healing.test.ts -t recoverStrandedCompletedTodoTasks → 4 passed vitest run merger.test.ts merger-finalize-unproven.real-git → 20 passed vitest run self-healing-workspace + workspace-merger-lease + workspace-merger-deps-resilient → 26 passed ``` New tests: - merger-ai.test.ts: commit-expected empty (reverted) → blocked to todo + error + audit event, NOT done; empty + prior no-op proof → still no-op done; empty + branch-ancestor-of-main → still no-op done; noCommitsExpected empty → unchanged done path. - workspace-merger.test.ts: all-empty (reverted) workspace → blocked to todo + error, not done / not `task:merged`. - self-healing.test.ts: a task blocked by this guard (all steps done/skipped, `task.error` set) is NOT re-promoted by `recoverStrandedCompletedTodoTasks`. **`pnpm verify:fast` is red on this branch due to the pre-existing pi SDK breakage** (`auth-storage.ts`/`pi.ts`/`provider-registration.ts` — the FN-8142/FN-8145 `AuthStorage`/`ModelRegistry` removal that is the root of the FN-8141 incident). Verified those identical build errors reproduce with my changes stashed; this PR adds **zero** new type errors (no build error is in `merger-ai.ts` or `run-audit.ts`). 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-authored-by: Claude Opus <noreply@anthropic.com> |
||
|
|
19eb179473 |
fix(core): block empty-diff finalize of tasks with skipped steps — generalized FN-6461 guard (#2254)
## What & why FN-8141 (\"Update pi SDK to latest and verify Kimi K3 end to end\") was **laundered into `done` despite producing zero net changes**. The pi SDK bump kept breaking `verify:fast`, the work was reverted 5×, and the agent used the sanctioned skip affordance to mark **Testing & Verification** and **Documentation & Delivery** `skipped`. `isTaskComplete()` counts `skipped` as complete, so: 1. self-healing `recoverStrandedCompletedTodoTasks` promoted the todo task to in-review (all steps done/skipped), 2. the AI merger saw an empty diff vs main → \"finalizing as no-op\" → `done` with `mergeConfirmed:true`, 3. no reviewer ever saw it (skipped steps request no review; the merge-review pass reviews an empty diff). The only existing guard, `evaluateNoCommitsNoOpFinalize` (FN-6461), missed it **twice**: it only fired when `noCommitsExpected === true` (FN-8141 was commit-expected — the branch was empty because work was *reverted*), and even then only blocked when `incomplete >= done` (FN-8141 had 3 done vs 2 skipped). ## The fix Generalize the guard (same exported name/shape — every finalize lane keeps working) so a **zero-diff/no-op finalize is blocked whenever ANY step is `skipped`**: - a **verification-flavored** skipped step (name matching `/test|verif|qa|review/i`) blocks **unconditionally**; - any **other** skipped step blocks **unless** every non-skipped step is `done` **AND** the task is the legacy `noCommitsExpected` ops shape; - the legacy FN-6461 ratio rule (`noCommitsExpected` + `incomplete >= done`) is preserved for pending/in-progress incomplete work; - blocked evaluations return a precise `reason` naming the skipped steps. Legitimate shapes still pass: all-done no-skip empty diffs (left to the lineage-proof work), zero-step tasks, and `noCommitsExpected` ops tasks with a minor non-verification skipped tail. ## Surface enumeration The guard is the single chokepoint used at every zero-diff finalize lane; all already honor `.blocked`/`.reason`, so the core change fixes each surface: - `packages/engine/src/merger-ai.ts` ~1116 — AI empty-merge lane - `packages/engine/src/merger.ts` ~6261 / ~7354 / ~7658 — merger empty-own-diff + no-op lanes - `packages/engine/src/self-healing.ts` ~2851 — stranded-todo promoter pre-check; ~6335 — no-op review finalize Behavior on block is unchanged (error set, durable log entry, `task:no-commits-finalize-blocked-incomplete-steps` run-audit event, move back to todo with progress preserved). ## Test evidence - **Core** `pnpm --filter @fusion/core exec vitest run src/__tests__/no-commits-finalize-guard.test.ts` → **9 passed**. Covers FN-8141 shape (3 done + 2 skipped, not noCommitsExpected → blocked), verification-skip blocks regardless of ratio/`noCommitsExpected`, legacy `noCommitsExpected` shapes, all-done no-skip → not blocked, zero steps → not blocked. - **Engine lanes** — one test per finalize-lane family, all green: - `merger-ai.test.ts` (AI empty lane, incl. new FN-8141 reverted-commit-expected case) → **36 passed** - `merger-finalize-unproven.real-git.test.ts` (merger lanes) → passing - `self-healing.test.ts` (stranded-todo promoter + no-op review finalize, incl. new FN-8141 promoter case) → **394 passed** ### `pnpm verify:fast` — pre-existing engine build breakage (not this PR) `verify:fast` fails at the workspace-dist bootstrap because `@fusion/engine` does **not** typecheck on `main`: `src/auth-storage.ts`, `src/pi.ts`, `src/provider-registration.ts` reference `ModelRuntime` / `AuthInteraction` / `CredentialInfo` / private `ModelRegistry` members removed by pi 0.80.9/0.80.10 (the FN-8142 migration that motivated this incident; upstream fix is FN-8145). Verified this failure reproduces with my changes **stashed** (13 identical tsc errors at clean HEAD). This PR touches only `@fusion/core` (builds clean, `tsc` exit 0) and engine **test** files — no engine source — so it neither causes nor can resolve that breakage. 🤖 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** * Prevented empty or no-op finalization when verification, QA, review, or other required steps are skipped. * Ensured tasks with skipped work are not incorrectly marked complete, merged, or promoted during recovery. * Improved error messages to identify skipped verification steps blocking completion. * **Tests** * Added regression coverage across finalization, merge, and self-healing workflows. <!-- end of auto-generated comment: release notes by coderabbit.ai --> Co-authored-by: Claude Opus <noreply@anthropic.com> |
||
|
|
9a37415887 |
fix(engine): add honest blocked exit to fn_task_done so impossible tasks park failed instead of laundering to done (#2256)
## What & why
FN-8141 ("Update pi SDK to latest and verify Kimi K3 end to end") was
impossible as specced — pi 0.80.x removed `AuthStorage`/`ModelRegistry`
APIs, so every SDK bump broke the build. The executor correctly reverted
its work and filed follow-up FN-8145 — but had **no sanctioned way to
end the task in a blocked state**. `fn_task_done` only expressed
success: the bulk-completion gate refused it, the requeue budget re-ran
the doomed task 5 times, and the only remaining affordance (mark every
step `skipped`, then complete) made `isTaskComplete()` return true.
Self-healing then promoted the "complete" todo to in-review and the AI
merger finalized the empty diff as `done`. **The honest path must be
cheaper than the laundering path.**
This adds a first-class **blocked** outcome to the executor's
`fn_task_done` tool.
## Change
- `fn_task_done` gains `outcome: "completed" | "blocked"` (default
`"completed"`), optional `blockedBy: string[]`, and `reason` (required
when blocked).
- `outcome="blocked"` runs **before** every completion gate (completion
blocker, verdict providers, worktree invariants, bulk-completion
refusal) — blocked is not a completion claim, so none of those gates
apply.
- Parks the task `failed` with `error = "BLOCKED: <reason>"`, following
the FN-7863 `EXECUTION_DISPATCH_LOOP_EXHAUSTED` park convention: **steps
keep their true statuses** (no auto-done, no auto-skip), worktree/branch
preserved. It does **not** call `onDone()`, so the executor's existing
`status === "failed"` post-loop branch honors the park instead of
handing off to review.
- `blockedBy` is recorded as real `task.dependencies` edges (unioned
with existing) so the task requeues behind the blocker.
- Emits run-audit `task:execution-blocked-parked` with ids/outcomes-only
metadata (`taskId`, `blockedBy` ids, `hasReason` boolean — **never** the
reason prose).
- Executor + core prompt guidance and the
`bulk-step-completion-without-review` refusal message now name the
blocked exit as **the** correct action when work cannot proceed,
replacing skip-and-done. `PREMISE STALE:` skip guidance is preserved for
genuinely-stale premises.
## Surface enumeration
- **fn_task_done tool schema + handler**
(`packages/engine/src/executor.ts`): blocked branch added at the top of
`execute`, before all gates.
- **Refusal/requeue machinery**: `formatTaskDoneRefusal` for
`bulk-step-completion-without-review` now points at the blocked exit;
the requeue-budget path is untouched (blocked never enters it).
- **Executor prompt text**: turn-ending rules, the "Cannot proceed"
section, the preflight/stale-premise escape hatch (now explicitly
distinguishes stale-premise skip from blocked).
- **Core prompt mirror** (`packages/core/src/agent-prompts.ts`): same
turn-ending + cannot-proceed guidance.
- **Tool reference doc**
(`packages/cli/skill/fusion/references/engine-tools.md`): `fn_task_done`
params updated. (grep for `fn_task_done` confirmed the only executable
tool schema is in executor.ts; CLI/pi surfaces re-export it, no separate
schema copy.)
- **Self-healing**: verified a blocked-parked row is NOT auto-recovered
by `recoverStrandedCompletedTodoTasks` — its steps are not all
done/skipped and `task.error` is set (both are hard filters in the
sweep).
- **Run Audit inventory** (`AGENTS.md`): documented the new event.
## Test evidence
New `packages/engine/src/__tests__/executor-task-done-blocked.test.ts`
(8 tests) asserts the invariant across surfaces:
```
pnpm --filter @fusion/engine exec vitest run \
src/__tests__/executor-task-done-blocked.test.ts \
src/__tests__/executor-task-done-invariant.test.ts \
src/__tests__/gating-classifications.test.ts \
src/__tests__/reliability-interactions/execute-requeue-loop-guard.test.ts --reporter=dot
→ Test Files 3 passed | Tests 138 passed (0 failed)
```
Coverage: blocked parks failed with `BLOCKED:` error and does **not**
trip the bulk-completion refusal or requeue to todo; `blockedBy` unioned
into `dependencies`; `task:execution-blocked-parked` emitted with
metadata that excludes the reason prose; steps left untouched; empty
`reason` rejected without parking; `completed` outcome unchanged (still
marks steps done, no blocked audit); and
`recoverStrandedCompletedTodoTasks` never promotes a blocked-parked row.
### Note on `pnpm verify:fast`
`verify:fast` currently fails at the workspace build step due to
**pre-existing** type errors in `packages/engine/src/auth-storage.ts`,
`pi.ts`, and `provider-registration.ts` — the exact FN-8142 pi SDK API
break that FN-8145 will fix. These are present on the base branch and
untouched by this PR. Verified instead that this change introduces
**zero** new type errors (`tsc` diff before/after, engine and core both
clean) and that all scoped tests are green.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-authored-by: Claude Opus <noreply@anthropic.com>
|
||
|
|
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> |