7ddcf7e2a673e722adb195cf8a6fa765e622975d
4016 Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
844512ed3e |
FN-8976: prevent concurrent automatic validator runs
Prevent automatic validation from running alongside a fresh manual or non-memoized validator. - Serialize validator admission around a feature-scoped fresh-running check. - Report feature-versus-fingerprint blocking scope and preserve memoization semantics. - Add regression coverage, documentation, and a patch changeset. Files changed: .changeset/fn-8976-validator-admission.md | 7 ++ docs/missions.md | 4 +- .../__tests__/postgres/mission-store.pg.test.ts | 110 +++++++++++++++++++-- .../core/src/async-stores/async-mission-store.ts | 61 ++++++++++-- packages/core/src/missions/mission-types.ts | 2 + .../src/__tests__/mission-execution-loop.test.ts | 10 +- 6 files changed, 171 insertions(+), 23 deletions(-) Fusion-Task-Id: FN-8976 Fusion-Task-Lineage: 61b487e8-5542-4cce-94f7-58d5295fe177 Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai> |
||
|
|
90573e31b0 |
FN-8963: prevent concurrent mission validation runs
Prevent duplicate manual validation runs while preserving recovery from stale validator state. - Add atomic mission-store claims and release handling for manual validator runs. - Return a conflict response and disable the validation action while a run is active. - Cover concurrent claims, API behavior, UI state, and stale-window parity. Files changed: .changeset/fn-8963-validate-inflight-guard.md | 7 ++ docs/missions.md | 2 + .../__tests__/postgres/mission-store.pg.test.ts | 129 +++++++++++++++++++++ .../core/src/async-stores/async-mission-store.ts | 87 ++++++++++++-- packages/core/src/index.ts | 2 + packages/core/src/missions/mission-store.ts | 69 ++++++++++- packages/core/src/missions/mission-types.ts | 13 +++ packages/dashboard/app/api/legacy.ts | 2 + packages/dashboard/app/api/missions/missions.ts | 11 +- .../dashboard/app/components/MissionManager.tsx | 15 ++- .../MissionManager.validate-inflight.test.tsx | 77 ++++++++++++ .../mission-validate-inflight-guard.test.ts | 55 +++++++++ packages/dashboard/src/mission-routes.ts | 32 +--- .../src/__tests__/mission-execution-loop.test.ts | 61 +++++++++- .../validator-run-stale-window-parity.test.ts | 9 ++ 15 files changed, 549 insertions(+), 22 deletions(-) Fusion-Task-Id: FN-8963 Fusion-Task-Lineage: 7e5d0ae4-98ac-4f79-99ca-45fd1450362d Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai> |
||
|
|
cef07527f6 |
FN-8948: reconcile mission state from task ground truth
Centralize mission and feature state repair around deterministic task lifecycle evidence. - Add a reusable mission reconciliation authority with task-move, API, tool, and maintenance callers. - Repair unambiguous title links while preserving terminal, validation, and audit safeguards. - Route scheduler moves through the authority and reject duplicate feature-title ownership. - Expose reconcile controls and document the operator workflow. Files changed: .changeset/fn-8948-mission-auto-reconcile.md | 7 + AGENTS.md | 1 + docs/missions.md | 8 +- packages/cli/skill/fusion/SKILL.md | 2 +- .../cli/skill/fusion/references/extension-tools.md | 9 + .../skill/fusion/references/fusion-capabilities.md | 1 + .../extension-experiment-finalize.test.ts | 2 + .../__tests__/extension-gitlab-tracking.test.ts | 2 + .../cli/src/__tests__/extension-web-fetch.test.ts | 2 + packages/cli/src/extension.ts | 14 + packages/dashboard/src/mission-routes.ts | 24 +- .../src/__tests__/agent-mission-tools.test.ts | 2 +- .../engine/src/__tests__/mission-autopilot.test.ts | 8 +- .../src/__tests__/mission-state-reconcile.test.ts | 67 +++++ packages/engine/src/agent-tools.ts | 6 + .../engine/src/execution/gating-classifications.ts | 1 + packages/engine/src/index.ts | 6 + packages/engine/src/missions/index.ts | 8 +- packages/engine/src/missions/mission-autopilot.ts | 106 +------ .../engine/src/missions/mission-feature-sync.ts | 1 + .../engine/src/missions/mission-state-reconcile.ts | 169 +++++++++++ packages/engine/src/runtimes/in-process-runtime.ts | 4 +- packages/engine/src/scheduler.ts | 331 +++++---------------- packages/engine/src/util/run-audit.ts | 2 + 24 files changed, 426 insertions(+), 357 deletions(-) Fusion-Task-Id: FN-8948 Fusion-Task-Lineage: 0fbccef3-eeac-46d2-b3d8-aca679b3657e Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai> |
||
|
|
b82f1a41f3 |
FN-8947: add repairable mission validation controls
Add repairable mission-validation badges with clear and re-run flows. - Persist repair metadata and audit events for mission validation state changes. - Expose validation repair through dashboard routes, UI controls, engine tools, and CLI capabilities. - Add database, API, UI, engine, and extension coverage plus operator documentation. Files changed: .changeset/fn-8947-mission-validation-repair.md | 7 + docs/missions.md | 15 +- packages/cli/skill/fusion/SKILL.md | 2 +- .../cli/skill/fusion/references/extension-tools.md | 10 + .../skill/fusion/references/fusion-capabilities.md | 1 + .../extension-experiment-finalize.test.ts | 2 + .../__tests__/extension-gitlab-tracking.test.ts | 2 + .../cli/src/__tests__/extension-web-fetch.test.ts | 2 + packages/cli/src/__tests__/extension.test.ts | 24 +++ packages/cli/src/extension.ts | 40 ++++ .../mission-status-event-metadata.test.ts | 23 +++ .../postgres/mission-validation-repair.pg.test.ts | 187 ++++++++++++++++++ .../core/src/async-stores/async-mission-store.ts | 216 ++++++++++++++++++--- packages/core/src/index.gate.ts | 5 + packages/core/src/index.ts | 5 +- packages/core/src/missions/mission-types.ts | 49 +++++ packages/core/src/types.ts | 7 + packages/dashboard/app/api/legacy.ts | 1 + packages/dashboard/app/api/missions/missions.ts | 13 ++ .../dashboard/app/components/MissionManager.css | 26 ++- .../dashboard/app/components/MissionManager.tsx | 207 +++++++++++++++++--- .../__tests__/MissionManager.mobile-css.test.ts | 8 + .../MissionManager.validation-repair.test.tsx | 83 ++++++++ .../__tests__/mission-task-prefix-routes.test.ts | 162 +++++++++++++++- packages/dashboard/src/mission-routes.ts | 104 +++++++++- .../src/__tests__/agent-mission-tools.test.ts | 119 +++++++++++- .../__tests__/mission-feature-sync-lanes.test.ts | 59 +++++- .../workflow-step-readonly-allowlist.test.ts | 1 + packages/engine/src/agent-tools.ts | 36 ++++ .../engine/src/execution/gating-classifications.ts | 1 + packages/engine/src/index.ts | 1 + .../engine/src/missions/mission-feature-sync.ts | 79 ++++++++ 32 files changed, 1430 insertions(+), 67 deletions(-) Fusion-Task-Id: FN-8947 Fusion-Task-Lineage: b93e5ab6-021c-4d32-bffb-f89f4c3894bc Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai> |
||
|
|
f775039f5b |
feat(engine): add typed delivery-pipeline run-audit catalogue (#3410)
## What Adds a typed, queryable catalogue for engine run-audit events as the first step of the delivery-pipeline reliability & observability effort. - **New module** `packages/engine/src/run-audit/run-audit-catalogue.ts` — a typed registry describing run-audit event kinds (scheduler, self-healing, merger, worktree, symbol-lock, …) so pipeline observability can ingest and reason about them consistently. - **Parity test** `run-audit-catalogue.test.ts` — asserts the catalogue matches the emitted run-audit event space. - **Docs** `docs/run-audit.md` + index pointer. ## Why Run-audit events are currently emitted ad-hoc without a typed contract. A catalogue gives: - a single source of truth for event kinds/names, - a parity guard so any new or renamed event is caught, - a foundation for delivery-pipeline reliability dashboards. ## Verification - `@fusion/engine` `tsc` build → **PASS** - `vitest run run-audit-catalogue.test.ts` → **3 tests passed** - No production behavior change outside the new module. ## Scope New isolated module + its test + docs. No changesets/release artifacts. <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **Documentation** * Added a run-audit catalogue covering delivery-pipeline finalization, self-healing reconciliation, and durable-agent error events. * Documented recorded outcomes, emission conditions, audit-store querying, and event catalogue maintenance. * Added a documentation index entry linking to the new catalogue. * **Tests** * Added validation to ensure documented audit events remain complete, consistently formatted, and synchronized with the supported event catalogue. <!-- end of auto-generated comment: release notes by coderabbit.ai --> --------- Co-authored-by: Fusion <noreply@runfusion.ai> |
||
|
|
8c055a6695 |
fix: keep self-healing sweeps alive across stale task links (#3417)
## Summary Supersedes #3405 — the fork head is not writable from maintainers, so this branch carries the same fix rebased/merged onto current `main`. ## Conflict resolution - Main already landed the equivalent fail-open path as **FN-8919** (`readLinkedTaskOrUndefined` + per-agent try/catch). - Kept the additional `recoverAgentsRunningOnInactiveTasks` regression that covers task-gone races plus transient lookup isolation. - Dropped the duplicate changeset (main already has `fn-8919-agent-link-sweep-fail-open`). ## Test plan - [x] `git merge-tree` clean against `main` - [ ] CI green Closes context from #3405. <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **Bug Fixes** * Improved recovery handling when task lookups fail. * Agents linked to deleted or missing tasks are now unlinked, while agents affected by temporary errors remain preserved. * Recovery continues for other eligible agents instead of stopping after an individual lookup failure. * **Tests** * Added regression coverage for deleted, missing, and temporarily unavailable tasks. <!-- end of auto-generated comment: release notes by coderabbit.ai --> Co-authored-by: Codex <codex@openai.com> |
||
|
|
d749a11a5b |
FN-8923: document orphan merge durable-write fences
Record the bounded cancellation frontier for orphaned AI merge bodies. - Add AST-derived durable-writer inventory and drift guard for the pinned merge closure. - Characterize orphan and successor merge-lane writes with production-path fixtures. - Document unresolved follow-ups and link the fence from the architecture guide. Files changed: docs/architecture.md | 2 +- .../merge-orphan-body-durable-write-fences.md | 92 + .../__tests__/_merge-durable-write-callsites.ts | 938 ++ .../merge-orphan-durable-write-inventory.json | 9965 ++++++++++++++++++++ .../merge-orphan-body-durable-writes.test.ts | 348 + ...ge-orphan-durable-write-inventory-drift.test.ts | 56 + packages/engine/src/merge/merger-ai.ts | 6 + packages/engine/src/project-engine.ts | 6 + 8 files changed, 11412 insertions(+), 1 deletion(-) Fusion-Task-Id: FN-8923 Fusion-Task-Lineage: 5b2d278c-1626-439a-affd-077f2de2aef1 Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai> |
||
|
|
a839c61929 |
FN-8926: expose graph and recall through MCP
Expose Fusion knowledge graph and durable recall through a built-in MCP transport. - Add the reserved fusion-memory server with graph and recall MCP tools. - Resolve built-in availability and enable/disable tombstones across configuration and UI. - Add CLI transport, documentation, release metadata, and lane coverage tests. Files changed: .changeset/fn-8926-memory-mcp-server.md | 7 + docs/cli-reference.md | 4 + docs/mcp.md | 10 ++ packages/cli/src/bin.ts | 6 + .../__tests__/mcp-memory-server-spawn.test.ts | 79 ++++++++++ .../commands/__tests__/mcp-memory-server.test.ts | 83 +++++++++++ packages/cli/src/commands/__tests__/mcp.test.ts | 27 +++- packages/cli/src/commands/mcp-memory-server.ts | 104 +++++++++++++ packages/cli/src/commands/mcp.ts | 64 ++++++-- packages/core/package.json | 10 ++ .../core/src/__tests__/mcp-builtin-servers.test.ts | 17 +++ packages/core/src/__tests__/mcp-config.test.ts | 12 ++ packages/core/src/config/mcp-builtin-descriptor.ts | 16 ++ packages/core/src/config/mcp-builtin-servers.ts | 18 +++ packages/core/src/config/mcp-config.ts | 40 +++-- packages/core/src/config/mcp-discovery.ts | 3 +- packages/core/src/index.ts | 6 + packages/core/src/memory/index.ts | 1 + .../mcp/__tests__/memory-mcp-handler.test.ts | 36 +++++ .../mcp/__tests__/memory-mcp-serialization.test.ts | 23 +++ packages/core/src/memory/mcp/index.ts | 4 + .../core/src/memory/mcp/memory-mcp-backends.ts | 39 +++++ packages/core/src/memory/mcp/memory-mcp-handler.ts | 54 +++++++ .../src/memory/mcp/memory-mcp-serialization.ts | 48 ++++++ packages/core/src/memory/mcp/memory-mcp-tools.ts | 64 ++++++++ packages/core/src/types.ts | 10 ++ .../settings/sections/GlobalMcpSection.tsx | 14 +- .../settings/sections/McpServersCard.tsx | 67 +++++++-- .../settings/sections/ProjectMcpSection.tsx | 15 +- .../__tests__/McpServersCard.builtin.test.tsx | 45 ++++++ .../dashboard/src/__tests__/chat-manager.test.ts | 24 +++ .../register-config-mcp-pi-settings-routes.ts | 20 ++- packages/dashboard/vitest.config.ts | 2 + .../__tests__/mcp-builtin-lane-coverage.test.ts | 163 +++++++++++++++++++++ packages/engine/src/mcp/mcp-resolution.ts | 10 +- packages/engine/vitest.config.ts | 2 + 36 files changed, 1097 insertions(+), 50 deletions(-) Fusion-Task-Id: FN-8926 Fusion-Task-Lineage: b2861491-33da-4b05-b9f8-a7c1448c1c8c Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai> |
||
|
|
3143f9536f |
FN-8908: auto-recover terminal task failures
Recover generic terminal task failures through a bounded, durable retry budget before escalating them to operators. - add fenced task-store recovery claims, retries, budget resets, and audit events - defer terminal-failure notifications until recovery is exhausted while preserving a single escalation - expose operator retry budget reset and cover recovery lifecycle behavior Files changed: .../fn-8908-terminal-failure-auto-recovery.md | 7 + AGENTS.md | 1 + docs/agents.md | 2 + docs/architecture.md | 2 + packages/cli/src/commands/task.ts | 2 + packages/cli/src/extension.ts | 2 + ...terminal-failure-auto-recovery-store.pg.test.ts | 108 +++++++++ .../terminal-failure-auto-recovery.test.ts | 60 +++++ packages/core/src/index.gate.ts | 1 + packages/core/src/index.ts | 1 + packages/core/src/store.ts | 179 ++++++++++++++- .../core/src/task-store/archive-lifecycle-2.ts | 15 ++ packages/core/src/task-store/moves.ts | 48 +++- packages/core/src/task-store/persistence.ts | 18 +- packages/core/src/task-store/project-store-ops.ts | 4 +- .../src/task-store/workflow-task-create-ops.ts | 4 +- packages/core/src/tasks/index.ts | 1 + .../src/tasks/terminal-failure-auto-recovery.ts | 114 ++++++++++ packages/core/src/types/task/task-core.ts | 24 ++ .../src/routes/register-task-workflow-routes.ts | 2 + ...-healing-terminal-failure-auto-recovery.test.ts | 199 ++++++++++++++++ .../__tests__/notification-service.test.ts | 86 ++++++- .../__tests__/task-wedge-notification.test.ts | 2 +- .../src/notification/notification-service.ts | 103 +++++++-- .../src/notification/task-wedge-notification.ts | 30 ++- packages/engine/src/self-healing.ts | 251 ++++++++++++++++++++- packages/engine/src/util/run-audit.ts | 7 + 27 files changed, 1240 insertions(+), 33 deletions(-) Fusion-Task-Id: FN-8908 Fusion-Task-Lineage: 99e96b16-0306-41f1-87da-8623d69735f7 Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai> |
||
|
|
55fd20e5f8 |
fix(engine): bound spec-drift reconciliation so boot cannot exhaust the connection pool
Fusion wedged on "starting" and never brought the engine up. The dashboard bound
the migration holding server on 4040, then every query behind it failed with
"sorry, too many clients already", so the card never progressed and the
supervisor crash-looped.
Root cause: each spec-drift reconcile costs a DEDICATED PostgreSQL connection.
persist -> appendSpecDriftReport -> withPlanningLifecycleLock opens its own
postgres(directUrl, { max: 1 }) session, because the planning advisory lock is
session-scoped and deliberately fences a stale report against a newer plan.
enqueue() released every id straight into its own microtask, and project-engine
enqueues every task at runtime-boundary setup (listTasks includeArchived). On a
1,082-task project that opened ~1,082 lock sessions simultaneously against
max_connections = 500. The cluster saturated ~25s into boot and stayed saturated.
The flat 1s retry then made it self-sustaining rather than transient: once
saturated, every task failed for the same shared reason and re-armed in lockstep
once per second, re-opening the whole fleet of sessions and pinning the very
resource it was waiting on. Measured 4,777 lock sessions in 17 seconds.
Fix, contained to the reconciler — the advisory lock and its fencing semantics
are load-bearing and unchanged:
- concurrency bound (maxConcurrent, default 4) drained by a fair
insertion-ordered pump, so fan-out can no longer exceed a known connection cost
- per-task in-flight dedupe; two passes on one task would contend on that task's
own advisory lock while holding two connections
- exponential backoff with jitter capped at 60s, and retries re-enter through
enqueue so a retry storm is bounded by the same limit as a first pass
Verified against the real 1,082-task project: connections stay flat at 3-10
across a 70s boot that previously reached 1,109 and saturated, and the engine
boots through to executing tasks and shuts down cleanly.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
||
|
|
7b3bd75268 |
fix(engine): wire the Plan Review replan cap and tombstone its dead predecessor
Investigating the "dead cap" turned up the opposite of what it looked like, plus a worse problem next to it. The Plan Review replan loop was NOT unbounded. U3 re-owned the cap-park in the graph: requestPreMergeOptionalStepFix parks via parkPlanReviewReplanCapExhausted at awaiting-approval with reason plan-review-replan-cap, on both an explicit finite budget and the unbounded default. That capability has been live throughout. What was actually dead: 1. PLAN_REVIEW_GATE_REPLAN_CAP = 8 — an unread constant belonging to the out-of-graph triage gate (runPlanReviewBeforeExecution) that U10/R4 deleted. Its companion column Task.planReviewReplanCount was persisted, serialized and reset but never incremented or compared. A constant and a column that look like a live safety ceiling while enforcing nothing are worse than no ceiling: they answer "is this loop bounded?" with a confident yes. Deleted, ratcheted in legacy-tombstones.test.ts, and the column documented as legacy/never-written with the live owner named. 2. planReviewReplanCap — an operator-facing setting, declared, validated, documented in settings-reference.md and editable in the Workflow Editor, that NOTHING read. Lowering it changed nothing. The unbounded backstop was instead hardcoded to PLAN_REVIEW_FEEDBACK_HISTORY_LIMIT — a bound on how much reviewer PROSE is replayed into the next planning prompt, whose own comment says it is "bounded independently of persistence and retry accounting". Two unrelated concerns shared one number, so trimming prompt history would have silently tightened a safety ceiling. The backstop now resolves from the setting, defaulting to the new DEFAULT_PLAN_REVIEW_REPLAN_CAP = 15 — the previously-effective value, so this is a pure re-wiring rather than a silent behavior change. The existing 15-attempt regression test passes unchanged, which is the evidence for that. 0 is honored as park-on-first-REVISE. An explicit planReviewMaxRevisions / node maxRevisions budget remains a stricter, earlier gate. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> |
||
|
|
c05d44d44a |
fix(engine): bound planning retries and cap the planning turn
Three changes to the triage planning path.
1. Unclassified planning failures are bounded. specifyTask's catch-all branch —
the one reached by every error the classifiers above do not recognize — restored
the card's claimable status and wrote nothing else: no counter, no
nextRecoveryAt, no park. Triage rediscovery re-admitted the card on the very next
poll, and replaceActiveTaskWorkflowContinuation replaced the terminal work item
with a fresh one carrying no attempt count, so nothing recorded that the task had
already failed N times. It now consumes the same recoveryRetryCount/nextRecoveryAt
budget the transient branch uses (MAX_RECOVERY_RETRIES = 3, 60s/120s/300s jittered
backoff) and parks status:"failed" with a PLANNING_FAILED_EXHAUSTED: error once
spent — status:"failed" is what suppresses rediscovery. Classifying one error
string fixes one symptom; this budget is what makes the NEXT unrecognized error
fail safely instead of looping for a day.
2. The planning turn has a ceiling. Fusion set no timeout on it at all:
workflowStepTimeoutMs covers pre-merge workflow steps only, and the provider SDK's
300s APIConnectionTimeoutError caps time-to-first-byte and is cleared once headers
arrive, after which the stream is uncapped. configureHttpDispatcher, which would
install undici idle timeouts, is only called from pi's CLI entrypoints and never
in the in-process engine. Observed consequence: single attempts ran to 126 minutes,
with failed-attempt durations spread smoothly from 1 to 126 min and no clustering —
the signature of nothing enforcing a bound. New workflow-native planningTimeoutMs
(default 90 min) aborts the session; the failure consumes one bounded attempt.
The default is deliberately generous rather than tight. Successful planning work
items measured over 7 days ran p50 12.7 / p90 39.5 / p99 105.7 minutes, so a
tighter bound would abort legitimate plans and pay for the restart — the churn
this work exists to remove. It bounds hung turns, not slow ones.
3. [event:task:moved] executor tracing dropped from log to debug. It fires on
every dispatch, rebound, requeue, archive and self-healing move across every task,
which made it the loudest line in engine output and buried operator-actionable
events. No test pins the level; the information remains at debug.
Also fixes a test break shipped in
|
||
|
|
81139dbbd0 |
fix(engine): classify provider request timeouts as transient
"Request timed out." is the literal default message of the Anthropic and OpenAI
SDKs' APIConnectionTimeoutError, surfaced to Fusion by checkSessionError after
pi-coding-agent exhausts its in-session retries. It matched none of the
connection-scoped timeout patterns, which deliberately excluded "general
timeouts", so it fell through to specifyTask's generic failure branch — the one
that restores status: null and writes no counter, no nextRecoveryAt, and no park.
Triage rediscovery then re-admitted the card on the very next poll, forever.
Measured before this change: 48 "Specification failed: Request timed out." events
across 10 tasks in 30 hours with zero backoff between attempts. FN-8950 alone
burned 8 consecutive attempts over ~8 hours and never reached implementation.
Across 2 days, 91 failed planning attempts averaged 33 minutes each — ~50 hours of
wall-clock producing nothing, 24% of all planning time.
Classifying these as transient routes them into the bounded recovery policy
(MAX_RECOVERY_RETRIES = 3, 60s/120s/300s jittered backoff) already used by the
connection-level patterns, so a provider blip costs three spaced retries instead
of an unbounded loop.
The pattern is anchored to "request timed out" rather than a bare timeout match:
agent log prose and verification output legitimately contain "timed out"
("BuildKit timed out", "stuck-kill unwind timeout"), and a broad pattern would
reclassify real permanent failures as retryable — the mistake the connection-only
rule was written to avoid. Regression tests pin both directions.
This does not affect model fallback, which pi decides internally and Fusion only
observes.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
||
|
|
00ddafd5fe |
FN-8845: persist deterministic spec alignment
Persist approved-plan drift alignment on linked mission features. - Store spec alignment across PostgreSQL and SQLite mission feature projections - Reconcile and render durable alignment instead of browser-side task joins - Preserve migration identities and cover drift persistence regressions Files changed: docs/architecture.md | 2 + docs/missions.md | 2 +- .../core/src/__tests__/planner/spec-lock.test.ts | 1 + .../src/__tests__/postgres/schema-applier.test.ts | 9 ++++- .../async-stores/async-mission-store-queries.ts | 5 +++ packages/core/src/missions/mission-store.ts | 9 ++++- packages/core/src/missions/mission-types.ts | 10 +++++ packages/core/src/planner/spec-lock.ts | 7 +++- .../0053_mission_feature_spec_alignment.sql | 3 ++ packages/core/src/postgres/schema-applier.ts | 13 +++++- packages/core/src/postgres/schema/project.ts | 2 + packages/dashboard/app/api/missions/missions.ts | 2 + .../dashboard/app/components/MissionManager.tsx | 40 ++++++------------ packages/dashboard/app/components/mission-types.ts | 2 + .../src/__tests__/plan-approval-status.pg.test.ts | 4 +- .../src/__tests__/mission-feature-sync.test.ts | 37 ++++++++++++++++- .../src/__tests__/spec-drift-reconciler.test.ts | 14 +++++++ packages/engine/src/missions/mission-autopilot.ts | 17 +++++--- .../engine/src/missions/mission-feature-sync.ts | 47 +++++++++++++++++++++- packages/engine/src/project-engine.ts | 3 +- packages/engine/src/scheduler.ts | 40 +++++++++++------- packages/engine/src/spec-drift-reconciler.ts | 5 +++ 22 files changed, 215 insertions(+), 59 deletions(-) Fusion-Task-Id: FN-8845 Fusion-Task-Lineage: d4a30472-f1c3-41ba-a61b-3f2be1ad32ab Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai> |
||
|
|
963dba6f80 |
feat: gate review verdicts on finding severity and preserve remediation sessions
Review remediation loops were the dominant cost of task wall-clock: over 14 days,
tasks with >=5 post-review fix rounds were 22% of tasks but consumed 78% of all
task active time, and 311 of 331 recorded findings were spec-internal-consistency
complaints that changed no delivered behavior.
Two causes compounded. Plan/Code Review remediation was unbounded by default, and
the review policy ordered a full re-derivation of the artifact after every edit
("distrust the edit ... fresh holistic pass"), so each round surfaced a fresh crop
of previously-acceptable observations as new blockers.
Make the already-persisted WorkflowReviewFinding.severity load-bearing instead of
decorative: a REVISE only blocks when it carries a finding at or above the review
kind's threshold (plan: P0+P1, code: P0). Non-blocking findings are still parsed,
persisted, and handed to the implementer as advisory notes in PROMPT.md. Fails
closed — a REVISE with no findings, or with any unclassified finding, still blocks,
so prose-only and custom reviewers keep full blocking power. The gate only ever
relaxes a verdict, never promotes one.
Reviewer prompts now request the structured findings schema (Plan Review emitted
none before), define severity by consequence as P0/P1/P2, omit nits entirely rather
than filing them as low-severity findings, and use an incremental re-review contract.
Remediation renders findings grouped by priority and sanctions an explicit decline
with rationale, so a disputed finding has a terminal state.
Also preserve the implementation session across a review bounce: sendTaskBackForFix
no longer nulls sessionFile when preserving resume state, and the executor's finally
no longer clears it on a review handoff. Remediation rounds continue the conversation
instead of re-reading the repo and re-deriving the change they just wrote. The resume
prompt now directs a PROMPT.md re-read, without which a resumed agent would never see
the new findings.
New per-workflow settings planReviewBlockingSeverity / codeReviewBlockingSeverity;
set either to "any" to restore the previous behavior.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
||
|
|
e6b6223d30 |
fix(engine): stop per-poll symbol-lock renewal log and activityLog spam
Renewal runs every poll for every implementation-column task with declared symbols, and a lost lock never recovers by renewing — renewSymbolLocks reports the same lost set on each pass, so the warning and its store.logEntry companion repeated forever: log-pane spam plus unbounded activityLog growth for a stuck task. The two error paths had the same shape on any persistent failure. Extract the executor's suppression into a shared createRepeatSuppressedLog and use it in both: first occurrence per task/signature logs at full level, repeats drop to debug(), a changed lost set or error message logs again, and a clean renewal clears the memo. The logEntry write is gated on the same decision. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> |
||
|
|
a2deae041a |
fix(engine): stop per-poll executor dispatch-blocked log spam
The unmet-dependency and ephemeral-disabled pre-dispatch gates re-run on every dispatch attempt for a blocked task but only change state on the first, so every later pass re-logged the same line at default level and drowned the log pane. Route both through logDispatchBlockedOnce: first block per task/reason logs at log(), identical repeats drop to debug() (FUSION_DEBUG=executor), a changed reason logs again, and the marker clears when the gate passes. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> |
||
|
|
936dd1ea96 |
FN-8924: retain safe age gate for merge recovery
Document why interrupted merge recovery retains its age-based admission gate. - Record why merger logs cannot establish task ownership or orphan status. - Pin non-owner recovery behavior when a merging task was recently updated. Files changed: docs/self-healing-backward-move-audit.md | 8 +++++++- packages/engine/src/__tests__/self-healing.test.ts | 9 ++++++++- 2 files changed, 15 insertions(+), 2 deletions(-) Fusion-Task-Id: FN-8924 Fusion-Task-Lineage: 19e7f803-21fe-43fb-8ca6-eecf8ff61df4 Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai> |
||
|
|
0fc6f3d849 |
FN-8946: enable attributed agent mission status updates
Enable authorized agents to update feature and mission statuses with transactional, attributed audit events. - Add mission and feature status tools to the Fusion extension and engine allowlists. - Record bounded actor, reason, and hierarchy metadata for status transitions across every writer. - Guard linked feature transitions and document the agent-facing workflow. Files changed: .changeset/fn-8946-mission-status-writes.md | 7 + docs/missions.md | 7 +- packages/cli/skill/fusion/SKILL.md | 2 +- .../cli/skill/fusion/references/extension-tools.md | 20 +++ .../skill/fusion/references/fusion-capabilities.md | 2 + packages/cli/src/__tests__/extension.test.ts | 46 ++++++ packages/cli/src/extension.ts | 27 +++ .../mission-status-event-metadata.test.ts | 50 ++++++ .../__tests__/postgres/mission-store.pg.test.ts | 160 +++++++++++++++++- .../core/src/async-stores/async-mission-store.ts | 182 +++++++++++++++------ packages/core/src/index.ts | 5 + packages/core/src/missions/mission-store.ts | 7 +- packages/core/src/missions/mission-types.ts | 99 +++++++++-- .../src/__tests__/chat-toolset-permissions.test.ts | 24 +++ packages/dashboard/src/mission-routes.ts | 3 +- .../src/__tests__/agent-mission-tools.test.ts | 42 ++++- .../src/__tests__/heartbeat-executor.test.ts | 4 +- .../workflow-step-readonly-allowlist.test.ts | 2 + packages/engine/src/agent-tools.ts | 18 ++ .../engine/src/execution/gating-classifications.ts | 2 + 20 files changed, 635 insertions(+), 74 deletions(-) Fusion-Task-Id: FN-8946 Fusion-Task-Lineage: 473cc0e0-e632-48b3-ac84-adaaeb81db4b Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai> |
||
|
|
00e369711c |
FN-8949: add dead mock specifier guard
Prevent silent dead engine test mocks and restore renamed-lane coverage. - Add a ratcheting test that detects unresolved relative vi.mock specifiers. - Update self-healing test seams for moved modules and queue transitions. - Document mock-specifier and store-fake failure patterns. Files changed: .../dead-vi-mock-specifiers-fail-silently.md | 57 ++++++ ...e-defects-that-masquerade-as-production-bugs.md | 12 ++ docs/testing.md | 5 + .../self-healing-query-filter-blindness.test.ts | 42 ++++- .../__tests__/vi-mock-specifiers-resolve.test.ts | 199 +++++++++++++++++++++ 5 files changed, 306 insertions(+), 9 deletions(-) Fusion-Task-Id: FN-8949 Fusion-Task-Lineage: a1079d1c-a2a9-4ac2-8245-1db434468405 Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai> |
||
|
|
0fbeba50d1 |
FN-8937: rescue project engine test quarantine
Rescue the project engine suite by making subprocess watchdog behavior deterministic. - Capture real timer APIs for subprocess watchdogs and isolate failure ownership. - Mock integration-branch resolution to prevent host git during lifecycle tests. - Add watchdog regression coverage and remove the expired quarantine exclusion. Files changed: docs/testing.md | 3 + packages/core/src/__test-utils__/vitest-setup.ts | 74 ++++++++++- .../__tests__/subprocess-guard-fake-timers.test.ts | 140 +++++++++++++++++++++ .../engine/src/__tests__/project-engine.test.ts | 63 +++++++--- packages/engine/vitest.config.ts | 12 +- scripts/lib/test-quarantine.json | 8 +- 6 files changed, 265 insertions(+), 35 deletions(-) Fusion-Task-Id: FN-8937 Fusion-Task-Lineage: 9fe166b5-b101-4683-bb2b-4855ee73df10 Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai> |
||
|
|
08a3f2851b |
FN-8919: harden stale agent link recovery
Normalize missing task lookup errors so stale durable-agent links do not halt self-healing sweeps. - Treat thrown not-found and deleted task lookups as stale links. - Isolate transient task lookup failures to their affected agent. - Add recovery regression coverage and a patch changeset. Files changed: .changeset/fn-8919-agent-link-sweep-fail-open.md | 7 ++ .../self-healing-agent-link-drift.test.ts | 58 +++++++++++++-- .../self-healing-path-utils-task-miss.test.ts | 51 +++++++++++++ packages/engine/src/__tests__/self-healing.test.ts | 84 +++++++++++++++++++++- .../engine/src/healing/self-healing-path-utils.ts | 34 ++++++++- packages/engine/src/self-healing.ts | 57 +++++++++++---- 6 files changed, 272 insertions(+), 19 deletions(-) Fusion-Task-Id: FN-8919 Fusion-Task-Lineage: 6dca2c1d-8ff9-47fb-b6c1-24f5af57b499 Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai> |
||
|
|
e610c72034 |
FN-8943: reconcile spec-lock divergence history
Preserve prior divergence when a task is re-locked after plan changes. - Persist immutable spec locks, current-plan evidence, and drift reports. - Reconcile retained divergence into re-approved alignment state across engine, API, and dashboard views. - Fence Plan Review acceptance and schema upgrades while retaining migration identity parity. Files changed: .changeset/fn-8845-spec-lock-drift-report.md | 7 + .changeset/fn-8943-spec-lock-divergence.md | 7 + docs/architecture.md | 17 +- docs/dashboard-guide.md | 3 + docs/missions.md | 4 + .../core/src/__tests__/planner/spec-lock.test.ts | 158 ++++++++++++++ .../src/__tests__/postgres/schema-applier.test.ts | 30 ++- .../postgres/task-dependency-mutation.pg.test.ts | 74 +++++++ packages/core/src/index.gate.ts | 4 + packages/core/src/index.ts | 4 + packages/core/src/planner/drift-report.ts | 152 +++++++++++++ packages/core/src/planner/spec-lock.ts | 182 ++++++++++++++++ .../migrations/0050_spec_lock_drift_report.sql | 29 +++ .../0051_spec_lock_source_revision_bigint.sql | 3 + packages/core/src/postgres/schema-applier.ts | 25 ++- packages/core/src/postgres/schema/project.ts | 13 ++ packages/core/src/store.ts | 242 ++++++++++++++++++++- .../core/src/task-store/branch-and-pr-entities.ts | 32 ++- packages/core/src/task-store/project-store-ops.ts | 34 ++- packages/core/src/task-store/task-update.ts | 66 +++++- packages/core/src/task-store/update-task-deps.ts | 31 ++- packages/dashboard/app/api.ts | 3 + packages/dashboard/app/api/tasks/tasks.ts | 22 ++ .../dashboard/app/components/MissionManager.css | 21 ++ .../dashboard/app/components/MissionManager.tsx | 41 +++- .../dashboard/app/components/TaskDetailModal.css | 26 +++ .../dashboard/app/components/TaskDetailModal.tsx | 62 +++++- .../__tests__/TaskDetailModal.spec-lock.test.tsx | 80 +++++++ .../__tests__/TaskDetailModal.test-helpers.ts | 2 + .../src/__tests__/plan-approval-status.pg.test.ts | 81 ++++++- .../src/routes/register-task-workflow-routes.ts | 59 ++++- .../src/__tests__/mission-feature-sync.test.ts | 15 +- .../src/__tests__/spec-drift-reconciler.test.ts | 108 +++++++++ .../engine/src/executor/execute-workflow-graph.ts | 49 ++++- .../engine/src/missions/mission-feature-sync.ts | 55 ++++- packages/engine/src/project-engine.ts | 33 +++ packages/engine/src/spec-drift-reconciler.ts | 105 +++++++++ packages/engine/src/triage.ts | 29 +++ 38 files changed, 1842 insertions(+), 66 deletions(-) Fusion-Task-Id: FN-8943 Fusion-Task-Lineage: 2a7f8a38-99aa-4c4b-8c36-41d14c466d21 Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai> |
||
|
|
5b9e6643d6 |
FN-8869: inherit project model overrides for role agents
Role-based permanent agents now consistently use their effective project model and thinking settings. - Resolve permanent-agent model and thinking inheritance by workflow role in core. - Apply inherited settings to Agent views, Chat sessions, routes, and heartbeat sessions. - Cover role-specific inheritance and preserve the lifecycle-column ratchet classification. Files changed: .changeset/fn-8869-role-agent-project-model-override.md | 7 ++ docs/agents.md | 9 +- docs/settings-reference.md | 4 +- packages/core/src/__tests__/agent-effective-model.test.ts | 97 +++++++++++++++++++ packages/core/src/__tests__/no-hardcoded-lifecycle-columns.test.ts | 2 + packages/core/src/ai/agent-effective-model.ts | 82 +++++++++++++++++ packages/core/src/ai/model-resolution.ts | 14 +++ packages/core/src/index.gate.ts | 7 ++ packages/core/src/index.ts | 7 ++ packages/core/src/types.ts | 7 ++ packages/dashboard/app/components/AgentDetailView.tsx | 49 ++++++--- packages/dashboard/app/components/AgentsView.tsx | 11 ++- packages/dashboard/app/components/__tests__/AgentDetailView.effective-model.test.tsx | 55 +++++++++++ packages/dashboard/app/components/__tests__/AgentDetailView.settings.test.tsx | 22 +++++ packages/dashboard/app/components/__tests__/AgentsView.test.tsx | 25 +++++ packages/dashboard/src/__tests__/chat-manager.test.ts | 45 +++++++++ packages/dashboard/src/__tests__/routes-chat-sessions-project-model.test.ts | 103 +++++++++++++++++++++ packages/dashboard/src/chat.ts | 65 +++++-------- packages/dashboard/src/routes/register-chat-routes.ts | 37 +++++--- packages/engine/src/__tests__/agent-session-helpers-test-mode.test.ts | 13 +++ packages/engine/src/__tests__/agent-session-helpers.test.ts | 54 +++++++++++ packages/engine/src/agent-heartbeat.ts | 10 +- packages/engine/src/agents/agent-session-helpers.ts | 6 +- 23 files changed, 648 insertions(+), 83 deletions(-) Fusion-Task-Id: FN-8869 Fusion-Task-Lineage: 00227aaf-b33b-43f1-bcbb-2203c9930dbc Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai> |
||
|
|
51437558ac |
fix(workflow): give a stranded planning hold a retry owner, and stop status erasure
FN-8923 sat silent in Todo for 7+ hours with zero run-audit rows. Its plan node held on principal routing, triage correctly recorded `needs-replan`, and then dependency auto-unblock nulled that status when its blocker completed. From that moment the card was invisible to both lanes: triage saw a fully-written spec with no replan flag and skipped it, while the executor's `isUnplannedForExecution` refused to dispatch because no capacity-boundary continuation existed. Not stuck in a retry loop -- unowned. - Dependency auto-unblock clears only the `queued` marker it owns, at all four sites (scheduler.ts plus three in self-healing.ts). `status` is a shared lifecycle channel and `needs-replan` is the only signal that re-admits a hold-column card whose PROMPT.md is already a real spec. - New self-healing sweep `reconcilePrincipalHeldPlanningContinuations` re-queues planning for a card whose sole active continuation is a principal-routing hold. A planning hold otherwise has no retry owner at all. Gated on the planning lane, effective auto-merge, an owned (null) status, and the shared planning lifecycle lock, so it cannot clobber a triage claim or launder a `failed` / `stuck-killed` / `queued` card into a replan. - Workflow node-instance-id materialization is idempotent across foreach, loop, and optional-group containers. It re-wrapped its own output on every dispatch, so FN-8869 grew a ~1.8 KB `run_id` of ~30 repeated segments on a hot indexed column and every retry read as a distinct run. - An unresolvable node instance or absent IR now fails closed instead of being treated as an edited-away override -- the previous shape would have discarded a real reviewer fence and handed a named review to the pool. - Mirror the routing exports into the gate-safe core barrel; the reduced barrel resolved them to `undefined`, a latent trap for any suite reaching the router. Findings from a multi-reviewer pass; 9 of 11 confirmed by an independent validator. Each fix carries a regression asserting the invariant across its surfaces, not the single reported case -- the optional-group accretion test was verified to fail without the fix. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> |
||
|
|
37d32357e9 |
fix(workflow): keep assigned tasks executing instead of holding on a dead principal
Workflow principal routing conflated two different questions: whether an agent CAN run a node, and whether it can run it RIGHT NOW. Both produced a hold, and a named principal never falls through to the role pool — so an agent that could never satisfy the node wedged the task permanently. FN-8869, FN-8928, and FN-8845 were each explicitly assigned to a permanent engineer-role agent (which the assignment policy allows). Their `step-execute` nodes took that owner as `task-assignee` authority, found no `executor` tag, and held closed. Each card re-dispatched and re-held every ~15 minutes for hours while two idle `Workflow Executor` pool agents were never consulted. The only thing still touching them was the owner's hourly heartbeat, which logged "progressing, no blockers" and exited: heartbeat observation had replaced execution. - Structural incapability (wrong role, agent deleted, authority edited away) is no longer authority for the node. Routing continues to the column binding and then the role pool. - A resumed continuation whose fence proves stale discards it and re-routes, instead of re-asserting a dead principal on every dispatch. - Availability is unchanged and still fail-closed: a role-capable principal that is paused, disabled, or at session capacity holds, and is never silently replaced by a pool member. - An explicitly assigned engineer-role agent is now valid task-assignee authority for an executor node, so the assigned agent executes its own task continuously under graph dispatch. The role pool stays strict, since automatic backlog pickup by engineers is a separate opt-in. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> |
||
|
|
d9a2d9dba3 |
FN-8925: clear orphaned paused merge stamps safely
Allow only engine-owned deadlock pauses to clear stale merge stamps without resuming work. - Fail closed when merge ownership probing is unavailable. - Clear stale stamps only for merge-deadlock-detected pauses and suppress their enqueue. - Add race coverage, audit documentation, and a patch changeset. Files changed: .changeset/fn-8925-paused-stale-merge.md | 7 + docs/self-healing-backward-move-audit.md | 2 +- docs/task-management.md | 4 +- .../self-healing-query-filter-blindness.test.ts | 5 +- .../self-healing-stale-merge-fanout.test.ts | 1 + packages/engine/src/__tests__/self-healing.test.ts | 203 +++++++++++++++++++-- packages/engine/src/self-healing.ts | 45 ++++- 7 files changed, 241 insertions(+), 26 deletions(-) Fusion-Task-Id: FN-8925 Fusion-Task-Lineage: e5491e1a-ca0a-4a82-b4e0-4002f30e869e Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>, |
||
|
|
b30508c685 |
fix(executor): preserve external checkout ownership guards (#3404)
## Summary - keep operator-routed external checkouts out of managed worktree preflight, cleanup, and lost-work reconciliation paths - mark injected custom graph worktree creation as native so workspace mode accepts the managed backend - add an extraction regression guard for the ownership fences ## Test plan - `pnpm --filter @fusion/engine exec vitest run src/executor/__tests__/external-checkout-extraction-guards.test.ts --silent=passed-only --reporter=dot` - `pnpm --filter @fusion/engine typecheck` - `pnpm check:changesets` - targeted ESLint on the changed TypeScript files <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **Bug Fixes** * External execution checkouts are no longer treated as Fusion-managed worktrees. * Prevented unnecessary Git checks, cleanup, and reconciliation during retries, pauses, recovery, and stuck-task handling. * Invalid external checkout configurations now fail safely with an error. * Graph-injected worktrees now use the native worktree backend for consistent setup. * **Tests** * Added coverage verifying external checkouts remain excluded from managed worktree operations. <!-- end of auto-generated comment: release notes by coderabbit.ai --> |
||
|
|
a5c3476cb2 |
fix: harden pinned worktree recovery cleanup (#3402)
## Summary - serialize pinned-path classification, orphan preservation, quarantine reconciliation, and recreation under one reservation - preserve cross-filesystem orphans atomically beside the configured worktree root and retain the newest 10 generated entries per recovery root - exclude recovery containers from pool and self-healing scans, with fail-closed symlink and active-session guards - document recovery location and retention behavior ## Test plan - `pnpm --filter @fusion/engine exec vitest run src/__tests__/worktree-acquisition.test.ts src/__tests__/worktree-paths.test.ts src/__tests__/worktree-pool.test.ts src/__tests__/self-healing-tempdir-sweep.test.ts --silent=passed-only --reporter=dot` - `pnpm --filter @fusion/engine typecheck` - `pnpm --filter @fusion/engine build` - `pnpm test:gate:static` - `pnpm check:changesets --strict` - `pnpm check:fnxc-future-dates` <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **New Features** * Preserves orphaned pinned worktrees during recovery, including across filesystems. * Retains the 10 most recent recovery entries and safely skips active or invalid entries. * Keeps recovery data separate from normal worktree discovery, cleanup, and capacity checks. * Adds safeguards for path containment, active-session ownership, and concurrent recovery. * **Bug Fixes** * Prevents pinned worktree data from being lost during recreation or quarantine cleanup. * Ensures recovery cleanup failures do not interrupt worktree acquisition. * **Documentation** * Documented orphan recovery, retention, fallback behavior, and cleanup safeguards. <!-- end of auto-generated comment: release notes by coderabbit.ai --> |
||
|
|
1474c617b5 |
FN-8918: suppress false parked-task alerts
Prevent stale self-healing observations from notifying operators about active or intentionally held tasks. - Require ownerless self-healing proof before classifying a task as wedged. - Revalidate live tasks and suppress notifications for progressing, held, deleted, archived, or complete-lane rows. - Cover reviewing, typed not-found reads, and archived episode resolution; document the behavior. - Add a patch changeset for the notification fix. Files changed: .changeset/fn-8918-false-parked-task-alerts.md | 7 ++ docs/agents.md | 2 +- docs/architecture.md | 2 +- packages/engine/src/notification/__tests__/task-wedge-notification.test.ts | 121 +++++++++++++++++++-- packages/engine/src/notification/notification-service.ts | 34 ++++-- packages/engine/src/notification/task-wedge-notification.ts | 21 +++- 6 files changed, 166 insertions(+), 21 deletions(-) Fusion-Task-Id: FN-8918 Fusion-Task-Lineage: 4da385aa-c625-40c5-a781-36becdf94fe5 Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai> |
||
|
|
f5a9cf01b1 |
FN-8912: recover stale merge status after aborts
Clear orphaned transient merge statuses so bounded retries can proceed. - Fence aborted merge generations from writing transient task statuses. - Reconcile and clear unconfirmed merge stamps in the queue and stale-state recovery paths. - Cover timeout, abort, retry, and self-healing behavior with regression tests. Files changed: .changeset/fn-8912-merge-abort-transient-status.md | 7 + docs/architecture.md | 1 + docs/task-management.md | 2 +- .../merge-abort-clears-transient-status.test.ts | 410 +++++++++++++++++++++ .../src/__tests__/merge-active-status.test.ts | 21 ++ packages/engine/src/__tests__/self-healing.test.ts | 231 ++++++++++-- packages/engine/src/merge/merge-active-status.ts | 18 + packages/engine/src/merge/merger-ai.ts | 34 +- packages/engine/src/project-engine.ts | 49 ++- packages/engine/src/self-healing.ts | 72 +++- 10 files changed, 810 insertions(+), 35 deletions(-) Fusion-Task-Id: FN-8912 Fusion-Task-Lineage: 8b2b7506-ed3e-41d4-9e54-59f45abc0020 Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai> |
||
|
|
351c3b7e28 |
fix(executor): restore two fixes dropped by the U4 executor peel
PR #3317 rewrote executor.ts from a pre-change base. A refactor rebased off a
stale base does not conflict — it deletes. An audit of every commit touching
executor.ts before the peel found two landed fixes silently removed:
- FN-8850 completion recommendations: the `fn_task_done` VALIDATOR survived the
peel but the engine-appended prompt section asking the executor to produce
recommendations did not. Nothing failed — recommendations were still accepted,
no test covered the prompt wiring, and capture simply stopped. Restored
verbatim in executor/system-prompt.ts, next to the validator it pairs with.
- The WorktreeBaseRefreshError guard (
|
||
|
|
51a5e1c275 |
fix(agents): separate heartbeat runtime from workflow routability
runtimeConfig.enabled answered two different questions. Every consumer in the
engine reads it as "run this agent's own durable heartbeat loop" — heartbeat
scheduling, error recovery, self-healing, in-process runtime — except the
workflow router, which also read it as "may own a workflow stage".
That conflation caused both failures:
- Built-in owners ship with the heartbeat off, which is CORRECT (they are
invoked by the workflow engine and must not run autonomous loops or auto-claim
work). That silently made every built-in role unroutable and deadlocked the
board.
- Enabling the heartbeat to restore routing then gave four agents autonomous
loops and auto-claiming nobody asked for.
Separate the flag:
- runtimeConfig.enabled governs the heartbeat runtime ONLY
- isWorkflowPrincipalEligible answers routability, and treats the four built-in
role owners as routable structurally — there is no fallback if a role cannot
route, so "unroutable" is not a state an operator can meaningfully select
- provision built-ins { enabled: false, autoClaimRelevantTasks: false }
- paused/errored still outranks the exemption, so it can never resurrect a
broken principal
Removes the earlier write-seam coercion that forced enabled:true — the invariant
is now structural rather than fought for on every write.
Also re-applies the principal-hold backoff ladder (15s -> 5m, checked before
graph entry) into executor/execute-workflow-graph.ts. PR #3317's executor peel
rewrote executor.ts from a pre-change base and dropped it.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
||
|
|
1cf86baa1c |
refactor: package code organization wave 18 (executor pure peels) (#3317)
## Summary
Wave 18 continues the package code-organization program after wave 17
domain folders (U4 Slice A from
`docs/plans/2026-07-14-001-refactor-package-code-organization-plan.md`).
### What changed
Peel **pure, behavior-preserving** helpers out of
`packages/engine/src/executor.ts` into domain modules under
`packages/engine/src/executor/`, with **stable re-exports** from
`executor.ts` so deep imports and `vi.mock("../executor.js")` keep
working.
| New module | Symbols |
|------------|---------|
| `executor/task-done-refusal.ts` | `evaluateTaskDoneRefusal`,
`determineRevisionResetStart`, skip-bypass refusal helper |
| `executor/workflow-feedback-paths.ts` |
`extractReferencedPathsFromWorkflowFeedback`,
`isAlwaysAllowedScopeLeakPath`, `workflowPathMatchesDeclaredScope` |
| `executor/workflow-step-verdict.ts` |
`FUSION_WORKFLOW_STEP_CONVENTIONS_PREAMBLE`, `parseWorkflowStepVerdict`
/ `parseWorkflowStepOutput`, step outcome types |
| `executor/await-input-parse.ts` | `parseAwaitInputSentinel`,
`parseAwaitInputQuestionToolCall` |
| `executor/no-commit-eligibility.ts` | `getNoCommitEligibilityReason`
(+ prompt heuristics) |
`executor.ts` live LOC ~**22817 → ~22427** (first pure-peel batch; more
peels needed to approach the 2k cap).
### Shims
- `old path` `executor.ts` public exports → `new path` `executor/*.ts` →
delete-when consumer deep-imports are re-pointed (not this PR)
### Test plan
- [x] `@fusion/engine` typecheck
- [x] Oracle: task-done refusal, skip-bypass, workflow malformed
verdict, scope-leak allowlist, executor-step-session, executor-prompt
- [x] `vitest --project=engine-core` (merge-gate curated suite)
- [ ] CI merge gate
**Stack:** wave17 (merged) → **this PR**
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit
* **New Features**
* Improved recognition of workflow outcomes from structured and
conversational responses.
* Added support for extracting questions from await-input responses and
tool calls.
* Improved workflow feedback handling for referenced files and declared
scope patterns.
* Added clearer guidance for task execution, approvals, verification,
and available tools.
* **Bug Fixes**
* Prevented completion when required review approvals are missing or
revisions remain pending.
* Improved handling of workflows that legitimately require no code
changes.
* Added clearer refusal messages and more reliable revision restarts.
* Sanitized repository paths in Git remediation instructions.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
|
||
|
|
14c8793df5 |
fix(engine): auto-restart tunnels that exit unexpectedly (#3393)
## Symptom A tunnel process that dies unexpectedly (crash, OOM, network hiccup, the 24h `maxLifetimeMs` supervision cutoff) leaves `TunnelProcessManager` in a terminal `failed` state. `handleUnexpectedExit` records the error and stops — the operator's remote-access tunnel silently goes dark until a human notices and restarts it. ## Fix The manager now remembers the desired tunnel (`provider` + `config`) across the start/switch lifecycle and respawns it after an unexpected exit with exponential backoff — 1s base doubling to a 30s cap, both configurable via new `restartBaseDelayMs` / `restartMaxDelayMs` options, and `autoRestart: false` to opt out. The attempt counter resets once the tunnel reports ready. Intentional shutdowns stay shut down: `stop()` and `switchProvider()` clear the desired tunnel and cancel any pending restart timer. Hardening that falls out of restart support: - Exit/spawn-error/readiness-timeout handlers are guarded by process-handle identity, so a stale `close` event from a superseded child cannot clobber the state of the restarted tunnel. - The readiness-timeout path now SIGTERMs the stalled child instead of leaking it while marking the tunnel failed. - A synchronous `superviseSpawn` throw now records a redacted `start_failed` status instead of escaping unhandled. ## Verification - New tests (fake timers, no real waits): restart after unexpected exit for both cloudflare and tailscale providers; backoff progression and delay cap across repeated pre-readiness failures; explicit `stop()` cancels a pending restart. - `pnpm --filter @fusion/engine exec vitest run src/__tests__/tunnel-process-manager.test.ts` — 17/17 pass. - `pnpm verify:fast` — 14 steps green (scoped typecheck/build + CLI build + boot smoke). - Changeset included (`patch`, category `fix`). ## Provenance This is the tunnel half of a fork-side fix (FN-915) that has been running in production since July; the merge-blocker-preservation half of that same fix already landed upstream (present since v0.76.0-beta.0). Ported onto current `main` — the original patch applied cleanly. 🤖 Generated with [Claude Code](https://claude.com/claude-code) <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **New Features** * Tunnels now automatically restart after unexpected crashes. * Restart attempts use increasing delays up to a configurable maximum. * Automatic recovery is enabled by default and can be configured. * **Bug Fixes** * Prevented stale process events from triggering unwanted restarts. * Explicit stops and provider changes now cancel pending restart attempts. * Failed or unready tunnel processes are terminated cleanly. <!-- end of auto-generated comment: release notes by coderabbit.ai --> Co-authored-by: v <v@v.speedport.ip> Co-authored-by: Claude Fable 5 <noreply@anthropic.com> |
||
|
|
b28b6d1053 |
fix: fail closed on incomplete external checkout routes (#3401)
## Summary - fail closed when a persisted external remediation route lacks a concrete checkout path - verify recovery, remediation, dependency-abort cleanup, and completion validation use the live persisted task route - use unique missing-checkout fixtures and exact observed-path assertions ## Test plan - `pnpm --filter @fusion/engine exec vitest run src/__tests__/verify-worktree-invariants-missing.test.ts src/__tests__/executor-triage-column-audit.test.ts --silent=passed-only --reporter=dot` - `pnpm --filter @fusion/engine exec vitest run src/__tests__/executor-fast-mode-workflows.test.ts -t 'completed-task recovery captures the live external checkout|pre-merge remediation' --silent=passed-only --reporter=dot` - `pnpm --filter @fusion/engine typecheck` - `pnpm exec eslint packages/engine/src/executor.ts packages/engine/src/__tests__/verify-worktree-invariants-missing.test.ts packages/engine/src/__tests__/executor-triage-column-audit.test.ts packages/engine/src/__tests__/executor-fast-mode-workflows.test.ts` - `pnpm check:fnxc-future-dates` - `pnpm check:changesets --strict` - `git diff --check` <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **Bug Fixes** * Improved recovery for externally executed tasks by using the latest routing information instead of outdated task data. * External remediation now stops safely when a checkout location is missing or invalid, preventing execution in an unintended location. * Improved cleanup behavior to preserve operator-owned checkouts. * Enhanced validation and error reporting for missing or invalid checkout paths. * **Tests** * Expanded coverage for recovery, remediation safety, checkout ownership, and worktree validation scenarios. <!-- end of auto-generated comment: release notes by coderabbit.ai --> |
||
|
|
e573178e31 |
fix(agents): never let a built-in workflow role agent be unroutable
The board stopped moving. Work items churned held -> running -> held at ~3.5/sec across every task, pinning a core and writing ~19k workflowWorkItem audit rows/hour while nothing executed. Hold reason: workflow-principal-role-pool-exhausted:executor. provisionBuiltinWorkflowRoleAgents seeded the four permanent owners (triage, executor, reviewer, merger) with runtimeConfig.enabled=false, while the router's available() treats enabled===false as unavailable. The only permanent principals for every built-in role were unroutable BY CONSTRUCTION — shipped that way, so any instance without operator-created role agents deadlocks at its first workflow node. Nothing self-recovers: a pool only changes by operator action. Routability of these four is an invariant, not a setting. Unlike an operator's agent, disabling one does not opt an agent out — it removes the only thing that can run that stage, and there is no fallback. - seed built-ins enabled; converge existing rows on provisioning - enforceBuiltinWorkflowRoleRoutability coerces enabled back at the durable writeAgent seam, so no REST/UI/plugin/restore path can reintroduce the deadlock. Other runtimeConfig keys are preserved; operator-owned agents keep their off switch - share the static routability predicate (isWorkflowPrincipalEligible) between provisioning and the router so the two cannot drift apart again Also fix the spin itself: a principal hold had no cooldown, so the scheduler re-dispatched instantly and the run re-entered only to re-fence and re-park. It now records a backoff ladder (15s -> 5m) checked before graph entry, and logs once per distinct reason instead of every pass — the same self-recovering shape as holdForSessionContention. The hold never increments `attempt`, so no existing guard could ever fire. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> |
||
|
|
73f428c5bf |
fix: preserve orphaned pinned worktrees before recovery (#3380)
## Summary - recover task-ID-pinned worktrees when their directory remains but Git metadata/registration is gone - preserve the orphan directory under `.fusion/recovery/worktrees` before recreating the worktree - retain existing fail-closed behavior for active, foreign, repo-root, or out-of-root paths ## Problem A task-pinned worktree can lose `.git` metadata while leaving build artifacts behind. Fusion classifies that path as incomplete or unregistered, but then calls `git worktree remove --force`. Git cannot remove a directory it no longer recognizes as a worktree, so acquisition aborts and the scheduler can repeat the same recovery indefinitely. The observed reproduction left `.build` and `.swiftpm` under the pinned path after Git registration was gone. ## Fix For an inactive path that is both: 1. inside the configured worktree root, and 2. classified as incomplete or unregistered, hold the shared worktree-path reservation across classification, preservation, and recreation. Recovery directories are created one canonical, project-contained component at a time, then the orphan is atomically moved into `.fusion/recovery/worktrees` and the pinned worktree is recreated. Moving rather than deleting preserves any unknown task artifacts for operator inspection. Other classifications continue through the existing guarded Git-removal path. ## Verification - `pnpm --filter @fusion/engine exec vitest run src/__tests__/worktree-acquisition.test.ts --silent=passed-only --reporter=dot` — 30 passed - `pnpm --filter @fusion/engine typecheck` - `pnpm --filter @fusion/engine build` - `pnpm test:gate:static` - `pnpm check:changesets` - `git diff --check` <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit - **Bug Fixes** - Improved recovery of task-pinned worktrees when incomplete or stale directories occupy the expected location. - Preserves eligible inactive or unregistered worktree contents in a recovery area instead of deleting them. - Prevents unsafe recovery through symbolic links and handles concurrent recovery attempts reliably. - Supports recovery when directories span different storage devices. - Ensures interrupted or invalid worktree states can be recreated safely without disrupting active sessions. - **Documentation** - Added a patch changeset documenting the improved worktree recovery behavior. <!-- end of auto-generated comment: release notes by coderabbit.ai --> |
||
|
|
26ea9fd40a |
fix: preserve external checkout routing through recovery (#3400)
## Summary - keep persisted operator-routed external checkouts authoritative during executor recovery, remediation, verification, and cleanup - fail closed when a configured external route is invalid instead of falling back to a Fusion-managed worktree - prevent Fusion from cleaning up operator-owned external checkouts - add dashboard and executor regression coverage for the routing handoffs ## Test plan - `pnpm --filter @fusion/engine exec vitest run src/__tests__/verify-worktree-invariants-missing.test.ts` - `pnpm --filter @fusion/engine exec vitest run src/__tests__/external-execution-checkout.test.ts src/__tests__/executor-triage-column-audit.test.ts` - `pnpm --filter @fusion/engine exec vitest run src/__tests__/executor-fast-mode-workflows.test.ts -t 'external execution|authoritative executor route|completed-task recovery captures the live external|pre-merge remediation reuses the live external'` - `FUSION_DASHBOARD_DEEP=1 pnpm --filter @fusion/dashboard exec vitest run src/__tests__/routes-tasks-near-duplicate.test.ts -t 'PATCH external-checkout persists one clean Git checkout for execution and review'` - `pnpm --filter @fusion/engine typecheck` - `pnpm --filter @fusion/dashboard typecheck` - `pnpm test:gate:static` - `pnpm check:changesets --strict` <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit - **Bug Fixes** - Improved external checkout routing across execution, verification, recovery, retries, and remediation. - Operations now use the latest persisted checkout details, preventing stale routing information from directing work to the wrong location. - Invalid or missing checkout routes fail safely with clear verification errors. - External checkouts are protected from unintended managed worktree or branch cleanup. - **Documentation** - Clarified external checkout routing and validation behavior. <!-- end of auto-generated comment: release notes by coderabbit.ai --> |
||
|
|
a06a4988d9 |
fix(worktree): stop terminally failing tasks over a stale worktree base
A stale base is an optimization miss, not an execution failure. FN-8693's dispatch-time refresh refused dirty checkouts and own-commit rebase conflicts with executionSafe:false, and the refusal threw out of acquireTaskWorktree into execute()'s generic terminal sink — parking the task `failed` and paging the operator. Run-audit for 2026-08-01..09: 99 of 136 execution failures were these refusals (74 dirty-worktree, 25 stale-base-conflict), and the bounded non-parking lane built for them fired 0 times because it only ever saw refusals published as typed graph node values and no code node enables refreshStaleBase. 82 of the 99 landed within five minutes of "Task marked done by agent": they were code-review-remediation re-entries into execute() on the task's own warm worktree — exactly the checkout the refresh must leave alone. Dispatch-time rebase has no conflict resolution, so on a busy main it could only ever fail; the merge lane already rebases with AI arbitration before landing and deliberately leaves refreshStaleBase off. - refreshReusedWorktreeBase: dirty tree, own-commit conflict, unresolvable base and compensated persistence failures now return skipped/executionSafe — keep the local base and run. Only an unproven tree (failed compensation, so a half-rebased checkout may be on disk) still refuses. - Check whether a mutation is needed before consulting the working tree: a worktree already on the current base was refused just for carrying WIP. - executor: catch WorktreeBaseRefreshError first and route it into holdForWorktreeBaseRefresh, one shared non-parking lane the graph path now uses too, so the two entry points cannot drift. - run-audit: worktree:base-refresh-skipped separates a declined refresh from a genuine block. reset-to-base — the actual FN-8693 requirement — is preserved and tested. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> |
||
|
|
2286a7a378 |
fix: refresh dependent worktrees after local merges (#3381)
## Summary - apply Fusion's existing stale-base reconciliation to freshly reacquired and pooled execution worktrees - advance retained task branches to the current local integration commit after dependencies land - keep planning and Worktrunk behavior unchanged while preserving dirty/conflict fail-closed handling ## Problem A dependent task can be planned before its dependency lands. If the dependency merges and its branch is deleted, a later execution retry may recreate the dependent worktree from its already-existing task branch. That branch can still point at the pre-dependency commit. Fusion already refreshes reused execution worktrees, but fresh acquisition returned without calling the same reconciliation primitive. The dependent task therefore executed without the landed dependency output even though Fusion marked the dependency complete. ## Fix When `refreshStaleBase` is enabled, run `refreshReusedWorktreeBase` after a native fresh or pooled worktree is acquired and before cleanup, init, or session execution. Track the actual backend used by injected and fallback creators so a native fallback still refreshes while Worktrunk-managed paths remain excluded. The existing primitive: - resolves the current local integration branch without requiring a remote - resets branches with no task-owned commits - rebases branches with task-owned commits - blocks dirty or conflicting worktrees - persists the integration commit as `baseCommitSha` If refresh blocks a pooled checkout, clear the task's durable binding before releasing the checkout for reuse. Planning callers do not enable `refreshStaleBase`, so planning worktrees remain unchanged. ## Verification - `pnpm --filter @fusion/engine exec vitest run src/__tests__/worktree-base-refresh.test.ts src/__tests__/worktree-acquisition.test.ts --silent=passed-only --reporter=dot` — 34 passed - `pnpm --filter @fusion/engine typecheck` - `pnpm --filter @fusion/engine build` - `pnpm test:gate:static` - `pnpm check:changesets` - `git diff --check` <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **Bug Fixes** * Improved worktree acquisition by refreshing stale branches against the current integration branch. * Added refresh support for recreated, pooled, and native fallback worktrees. * Prevented task execution when refresh fails and safely released affected pooled worktrees. * Avoided unnecessary refreshes for newly created Worktrunk worktrees. * **Tests** * Added coverage for stale-base refresh behavior across supported acquisition scenarios. <!-- end of auto-generated comment: release notes by coderabbit.ai --> |
||
|
|
50311a88d6 |
test(engine): normalize routed session mock calls (#3396)
## Summary - normalize Vitest mock-call tuples before selecting graph implementation sessions - restore six clean-main executor assertions that otherwise misclassify a routed implementation call as missing ## Test plan - `pnpm --filter @fusion/engine exec vitest run --reporter=dot src/__tests__/executor-review-verdicts.test.ts src/__tests__/executor-worktree-liveness.test.ts` - `pnpm --filter @fusion/engine typecheck` - `pnpm test:gate:static` <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **Tests** * Updated workflow routing and worktree liveness tests to use normalized agent configuration when validating implementation-session selection. * Improved coverage for fresh worktrees, configured directories, and routed implementation sessions. <!-- end of auto-generated comment: release notes by coderabbit.ai --> Co-authored-by: gsxdsm <gsxdsm@users.noreply.github.com> |
||
|
|
477f3faf0a |
feat: support operator-routed external task checkouts (#3398)
## Summary - add an explicit API route that persists one clean external Git checkout for task execution and enforced review - fence execution to the checkout's persisted branch and fail closed when the route becomes invalid - allow completion invariants to validate explicitly routed checkouts outside the project worktree directory ## Test plan - `pnpm --filter @fusion/engine exec vitest run src/__tests__/external-execution-checkout.test.ts src/__tests__/review-checkout.test.ts src/__tests__/engine-no-blocking-shellout.test.ts --silent=passed-only --reporter=dot` - `pnpm --filter @fusion/engine exec vitest run src/__tests__/executor-fast-mode-workflows.test.ts -t 'prepares a persisted external execution checkout' --silent=passed-only --reporter=dot` - `FUSION_DASHBOARD_DEEP=1 pnpm --filter @fusion/dashboard exec vitest run src/__tests__/routes-tasks-near-duplicate.test.ts -t 'PATCH external-checkout' --project dashboard-api --silent=passed-only --reporter=dot` - `pnpm --filter @fusion/engine typecheck` - `pnpm --filter @fusion/dashboard exec tsc --noEmit` - `pnpm verify:fast` - `pnpm test:gate` (605 non-PostgreSQL tests pass; local PostgreSQL suites cannot authenticate because the configured client returns an empty password) <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **New Features** * Added support for routing task execution and review through operator-selected external Git checkouts. * External checkouts are validated for valid Git repositories, attached branches, clean status, and branch consistency. * Tasks can clear previously configured external checkout routing. * Valid routed checkouts are used directly without creating a separate worktree. * **Bug Fixes** * Invalid, incomplete, dirty, or mismatched checkout configurations now fail early with clear validation errors. * Missing tasks return the appropriate not-found response. <!-- end of auto-generated comment: release notes by coderabbit.ai --> Co-authored-by: gsxdsm <gsxdsm@users.noreply.github.com> |
||
|
|
ccebe5c5cf |
fix(engine): keep orphaned planning recovery alive (#3399)
## Summary Resolves merge conflicts from #3392 against current `main`. `main` already landed the core #3386 fix via FN-8909 (`includeArchived: false` live-row enumeration + per-task isolation). This PR rebases the remaining #3392 refinements on top of that: - Best-effort, secret-free audit emission (per-task and no-action) - Distinguish `no-eligible-orphan` vs `all-attempts-failed` / `no-finalization` no-action outcomes - Redacted `errorType=` warn logs so poisoned-row failures cannot abort the sweep or leak error prose Supersedes #3392 (fork head is not writable from this environment despite `maintainer_can_modify`). ## Test plan - [x] `pnpm --filter @fusion/engine exec vitest run src/__tests__/self-healing.test.ts --project engine-default --run -t "finalizeOrphanedPlanningSegments"` — 8 passed - [ ] CI PR checks green <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **Bug Fixes** * Improved recovery of orphaned planning segments when individual finalization attempts fail. * Recovery now continues successfully even if audit recording encounters an error. * Added clearer recovery outcomes, distinguishing cases where no segments qualify, all attempts fail, or only some segments are finalized. * Warning messages now provide structured error details without exposing sensitive information. <!-- end of auto-generated comment: release notes by coderabbit.ai --> Co-authored-by: Codex <codex@openai.com> |
||
|
|
9ce698699a |
FN-8917: add telemetry seams to merger test stores
Ensure merger test fixtures model session-usage telemetry so user-hold release coverage exercises the production lane. - Add emitUsageEvent fakes across merger AI test stores. - Assert the user-held branch-group member emits merger session telemetry when explicitly released. - Document fixture requirements and expected hold-path stderr. Files changed: .../src/__tests__/group-merge-coordinator.test.ts | 21 +++++++++++++++++++++ .../engine/src/__tests__/merger-ai-cleanup.test.ts | 1 + .../merger-ai-dependency-install.slow.test.ts | 1 + .../__tests__/merger-ai-push-after-merge.test.ts | 1 + .../src/__tests__/merger-ai-renamed-columns.test.ts | 6 ++++++ .../ai-merge-cleanup-enoent-idempotent.test.ts | 1 + 6 files changed, 31 insertions(+) Fusion-Task-Id: FN-8917 Fusion-Task-Lineage: aab96cdb-50ec-441f-80f5-944e8e1d70ab Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai> |
||
|
|
8a7ab1dedc |
FN-8911: allow direct database URLs for planning locks
Allow planning lifecycle locks to use non-pooled runtime PostgreSQL connections without a duplicate migration URL. - Select runtime-direct endpoints for direct DATABASE_URL connections and retain migration overrides for pooled endpoints. - Preserve lifecycle transport failures across triage retries and expose lock helpers through the core barrel. - Document the connection behavior and add resolver, advisory-lock, and triage regression coverage. Files changed: .../fn-8911-direct-database-url-planning-lock.md | 7 + docs/architecture.md | 2 +- docs/multi-project.md | 4 +- .../__tests__/postgres/backend-resolver.test.ts | 43 +++++- .../planning-lifecycle-advisory-lock.pg.test.ts | 28 +++- packages/core/src/index.ts | 2 + packages/core/src/postgres/advisory-locks.ts | 6 +- packages/core/src/postgres/backend-resolver.ts | 29 +++- packages/core/src/postgres/index.ts | 5 + ...ge-planning-lifecycle-transport-failure.test.ts | 159 +++++++++++++++++++++ packages/engine/src/triage.ts | 100 +++++++++++-- 11 files changed, 360 insertions(+), 25 deletions(-) Fusion-Task-Id: FN-8911 Fusion-Task-Lineage: ac2ec0d6-f409-40ba-a678-6dd7da7006dd Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai> |
||
|
|
3aa32a846c |
FN-8910: allow held tasks to remediate review findings
Allow pre-merge remediation to proceed under project-level merge holds while preserving task-level operator holds. - Restrict remediation holds to operator-authored task-level auto-merge settings - Record remediation and revision-budget refusals for parked review tasks - Keep fire-and-forget remediation failures in their review lane - Cover shared-branch recovery behavior and document the policy Files changed: .changeset/fn-8910-premerge-remediation-hold.md | 7 ++ docs/workflow-steps.md | 2 + packages/core/src/__tests__/task-merge.test.ts | 44 ++++++------- packages/core/src/merge/task-merge.ts | 20 +++--- .../__tests__/executor-graph-requeue-gate.test.ts | 60 ++++++++++++++++- ...cutor-live-branch-group-auto-merge-hold.test.ts | 63 ++++++++++++++++-- .../workflow-graph-optional-step-fix.test.ts | 24 +++++-- .../src/__tests__/workflow-task-runtime.test.ts | 10 ++- packages/engine/src/executor.ts | 76 ++++++++++++++++++---- 9 files changed, 244 insertions(+), 62 deletions(-) Fusion-Task-Id: FN-8910 Fusion-Task-Lineage: b5e10f87-67cf-4acf-b125-91be5ade17a8 Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai> |
||
|
|
6bd178bdcf |
FN-8864: add durable agent activity stream
Add a persisted, project-scoped agent activity feed with query and live delivery surfaces. - Store sequenced, attributed activity events with privacy-safe metadata and retention. - Emit activity across agents, workflow execution, reviews, approvals, merges, and recovery. - Provide paginated API history and resilient SSE tailing with coverage and documentation. - Renumber the activity migration to 0049 after reconciling main’s 0048 GitHub check-state migration. Files changed: .changeset/fn-8864-agent-activity-events.md | 7 + docs/architecture.md | 8 + docs/diagnostics.md | 4 + docs/storage.md | 1 + .../__tests__/agent-activity-attribution.test.ts | 21 + .../agent-activity-metadata-hygiene.test.ts | 60 +++ .../src/__tests__/agent-activity-writers.test.ts | 94 +++++ .../postgres/agent-activity-events.pg.test.ts | 57 +++ .../src/__tests__/postgres/schema-applier.test.ts | 34 +- packages/core/src/agents/agent-store.ts | 24 ++ packages/core/src/agents/approval-request-store.ts | 15 +- packages/core/src/index.ts | 4 + .../0049_fn_8864_agent_activity_events.sql | 24 ++ packages/core/src/postgres/schema-applier.ts | 15 +- packages/core/src/postgres/schema/project.ts | 19 +- packages/core/src/store.ts | 17 + .../core/src/task-store/agent-activity-outbox.ts | 75 ++++ .../src/task-store/async/async-agent-activity.ts | 71 ++++ packages/core/src/types.ts | 2 + packages/core/src/types/agents/agents.ts | 79 ++++ packages/dashboard/app/api.ts | 54 +++ .../src/__tests__/agent-activity-route.test.ts | 68 ++++ .../src/__tests__/sse-agent-activity.test.ts | 315 +++++++++++++++ packages/dashboard/src/routes/README.md | 2 +- .../src/routes/register-setup-activity-routes.ts | 19 +- packages/dashboard/src/sse.ts | 139 ++++++- .../src/__tests__/agent-activity-writers.test.ts | 442 +++++++++++++++++++++ packages/engine/src/executor.ts | 110 ++++- packages/engine/src/merger.ts | 15 +- packages/engine/src/self-healing.ts | 35 +- 30 files changed, 1809 insertions(+), 21 deletions(-) Fusion-Task-Id: FN-8864 Fusion-Task-Lineage: 4938f35b-a0bc-4eb3-905c-178fed859cc6 Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai> |
||
|
|
3ca5d4d643 |
FN-8913: prioritize older tasks in hold-release scheduling
Rank eligible hold-release candidates by priority, age, and task ID to prevent newer work from starving older peers. - Reuse the core priority-age-ID comparator for hold-release evaluation. - Add regression coverage for priority, age, dependency, and overlap scheduling behavior. - Document the production hold-release fairness order and add a patch changeset. Files changed: .changeset/fn-8913-older-first-scheduling.md | 7 ++ docs/architecture.md | 4 +- .../hold-release-priority-age-order.test.ts | 113 +++++++++++++++++++++ .../__tests__/scheduler-overlap-starvation.test.ts | 76 ++++++++++++++ packages/engine/src/execution/hold-release.ts | 17 +++- 5 files changed, 212 insertions(+), 5 deletions(-) Fusion-Task-Id: FN-8913 Fusion-Task-Lineage: 89910df9-9d3a-404c-85fa-f0797e8fa001 Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai> |
||
|
|
ad91795dac |
FN-8843: assign eligible executors to new tasks
Assign durable executor ownership during shared task intake. - Resolve eligible executor owners for API, dashboard, and reserved-ID task creation. - Preserve explicit assignments, reject invalid owners, and audit unresolved ownerless intake. - Cover routing and PostgreSQL intake behavior with regression tests and document the contract. Files changed: .changeset/fn-8843-intake-agent-assignment.md | 7 + docs/architecture.md | 6 + docs/workflow-steps.md | 12 +- .../postgres/create-task-reserved-id.pg.test.ts | 187 +++++++++++++++ .../__tests__/task-intake-owner-resolver.test.ts | 141 ++++++++++++ packages/core/src/store.ts | 29 ++- packages/core/src/task-store/task-creation.ts | 251 +++++++++++++++++---- .../core/src/tasks/task-intake-owner-resolver.ts | 239 ++++++++++++++++++++ .../dashboard/src/__tests__/routes-tasks.test.ts | 47 ++++ .../__tests__/task-create-intake-owner.pg.test.ts | 195 ++++++++++++++++ .../src/routes/register-task-workflow-routes.ts | 12 +- .../src/__tests__/workflow-agent-routing.test.ts | 19 +- .../engine/src/agents/workflow-agent-router.ts | 37 ++- 13 files changed, 1120 insertions(+), 62 deletions(-) Fusion-Task-Id: FN-8843 Fusion-Task-Lineage: b94728b4-d9e8-4a26-8fbc-7096eb797839 Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai> |