Commit Graph

3490 Commits

Author SHA1 Message Date
gsxdsm
b5707318b5 feat(engine): add force to fn_task_promote for agent-native override parity
fn_task_promote can now pass force:true to start execution when a task is still
waiting on planning or plan review, matching the dashboard's promote override.
The rejection message names the flag so a caller that hits the gate can decide,
and a forced release says the pending replan was cancelled rather than burying it.

Force stays opt-in per explicit promote request: the hold-release sweep and the
webhook event release have no force parameter, so FN-7648 still holds for every
automatic surface.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-07-25 20:02:14 -07:00
gsxdsm
82e0ce3132 fix(engine): reclaim task branches whose tip is an inherited foreign commit
The reclaim sweep's tip-already-merged arm vetoed on the branch tip's foreign
Fusion-Task-Id trailer alone. A task branch cut from the base that never
committed anything (planning aborted, card moved back to todo) points at the
PREVIOUS task's landed commit, so the veto fired on inherited metadata: the card
kept stale worktree/branch/baseCommitSha and re-logged
"already-merged rejected ... reason=foreign-task-tip" every sweep.

Hoist the merge-base diff proof already used by already-merged and
branch-misbound recovery into a shared foreignTipRejection helper and route all
three callers through it. Rejection still fires when the branch carries unique
content or the base already has this task's own commit.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-07-25 20:01:50 -07:00
gsxdsm
41d60f0355 feat(board): explain the unplanned promote rejection and let operators force past it
Promote on a held card printed the raw i18n key `board.rejection.unplannedForExecution`:
FN-8471 added the server-side code without a client case or catalog entry, so
translateRejection fell through to `t(messageKey, messageKey)`.

- Add the explicit rejection case (both translate helpers) plus the en catalog
  entry and secondary-locale stubs.
- promoteHeldTask(..., { force }) waives ONLY the unplanned-for-execution gate;
  hold membership, capacity and slot reservation still arbitrate. It clears a
  needs-replan/plan-review-unavailable status so triage rediscovery cannot pull
  the card back into the waived replan, and emits task:promote-forced-unplanned.
- POST /tasks/:id/promote accepts { force: true }; the board asks for explicit
  confirmation first and only offers the override for this rejection.

Force stays operator-only — the sweep, the webhook release and fn_task_promote
never set it, so FN-7648 still holds for every automatic surface.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-07-25 19:56:54 -07:00
gsxdsm
2560944663 chore(release): v0.74.0-beta.2
Version bump via changesets.
2026-07-25 16:16:07 -07:00
gsxdsm
d10d91bae5 fix(engine): stop planning when a card is withdrawn; sweep stale pre-execution worktrees
Withdrawing a card from planning (todo -> Ideas) now stops the work:
- triage aborts and disposes the planning session through the same path
  pause/delete already use, and clears status:"planning" so the planning badge
  goes away and the card reads as a plain idea again;
- the executor aborts in-flight graph work on any backward move out of
  todo/triage, so a Plan Review does not keep streaming against a card the
  operator pulled back;
- moving it back to todo needs no new code: the existing column wake fires and,
  with the status cleared, the card is an ordinary planning candidate again.

Pre-execution worktrees (planning acquires one now) are reclaimed two ways: an
immediate release on an explicit withdrawal, and a self-healing sweep
`reconcile-pre-execution-worktrees`. The sweep is deliberately timid — 30 days
of complete inactivity, and it skips anything active or waiting (todo,
executing, in-review, done, paused, carrying any status, blocked, or scheduled
for recovery). Every real safety condition lives in the executor: never
executed, no live session, clean branch, nothing uncommitted.

hasAdvancedPastPlanning no longer reads a worktree as execution evidence.
Planning owns a worktree now, so that signal would have made every planning
write skip; execution timestamps carry the meaning instead.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-07-25 15:20:17 -07:00
gsxdsm
168819b35d fix(engine): run every lane in the task worktree; contention is a wait, not a failure
Contention prevention (why tasks shared a path at all):
- Planning ran `tools: "coding"` at the repo root, so every planner had write
  tools in the operator's checkout and all planners shared one path. Planning
  now acquires the task's own worktree (TriageProcessor.acquirePlanningWorktree
  -> TaskExecutor.ensureTaskWorktreeForPlanning).
- Graph nodes with no worktree acquired one instead of falling back to rootDir,
  so Plan Review / Code Review / custom gates all run isolated. Plan Review
  re-acquires when its recorded worktree is gone, replacing FN-7996's
  run-from-the-repo-root degrade. Workspace projects are unchanged.
- Registration goes through acquireActiveSessionPath, which reclaims a leaked
  entry whose holder is provably dead and aged past the FN-5256 floor. A live
  holder still contends — real serialization is never clobbered.

Classification (the reported symptom):
- A lease held by another task is no longer a provider failure. It carries
  SESSION_CONTENTION_HOLD_VALUE, classifies transient, is excluded from
  isNonPlanDefectPlanReviewFailure, and stops burning the node's fast retries.
- The executor waits it out on a 10-attempt 5s->60s ladder and then leaves the
  task cleanly queued. There is no terminal branch: contention always ends, so
  parking would only ask a human to press Retry on a condition that fixed
  itself.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-07-25 14:59:53 -07:00
gsxdsm
e6b2da6cae fix(engine): let concurrent tasks run Plan Review on the shared repo root
Plan Review needs no worktree, so it runs rooted at the project root. The
activeSessionRegistry key was the bare root path, so the second task to reach
Plan Review hit ActiveSessionPathHeldByForeignTaskError ("path ... is held by
task FN-1398; task FN-1403 may not overwrite it"). That surfaced as a Plan
Review provider failure, burned the in-place retry budget against a hold no
retry could clear, and left the task parked.

Task-scope the registry key for any session rooted at rootDir, in every project
mode — the workspace fix already did this for the shared browse-root. Root
exclusivity protects nothing here: write-capable nodes are refused at the root
outright, and every isPathActive consumer guards removable worktree paths.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-07-25 13:55:54 -07:00
gsxdsm
a0496c175c chore(release): v0.74.0-beta.1
Version bump via changesets.
2026-07-25 10:08:15 -07:00
gsxdsm
10df734bd1 fix(core): seed the prompt for quick-add Start creates; instrument hold-release
Quick-add "Start" collapses create+promote into one request: it submits the
workflow id AND the post-intake `todo` column together, so the card lands in
`todo` having never sat in the workflow's manual intake column. The intake test
in task-creation.ts only matched `triage` or the resolved intake column, so the
card got generateSpecifiedPrompt — whose hard-coded boilerplate steps
("Implement the required changes") no planner ever wrote.

That stranded the card permanently: triage's todo-discovery admits a card only
when its PROMPT.md reads as a seed, so the placeholder spec was classified
"already planned" and never planned, while nothing could execute it either
(steps: []). It sat in Todo forever with no log line in any lane. Observed on
FN-8587.

Creates into `todo` on a manual-intake workflow (resolved intake is not the
legacy `triage`) now get the bootstrap seed. The pinned contract for a plain
direct create into todo on the default workflow — which intentionally keeps
generateSpecifiedPrompt — is untouched, and both create sites are fixed in step.

Also instrument the hold/release sweep, which had reasons but no timings:
per-task held duration reported on release, a per-sweep summary breaking out the
prefetch cost (a sequential await per non-archived task, so it scales with board
size rather than with held cards), and a warn when a sweep exceeds 2s — so a
"ready card doesn't move" delay can be attributed between poll cadence, sweep
cost, and a card genuinely queued on capacity.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-07-25 09:52:18 -07:00
gsxdsm
e712b6faea fix(engine): dispatch as soon as planning finishes
Closes the second half of the "started card does nothing" gap. Plan-in-place
workflows (Coding (Ideas)) finalize by clearing `status` in place — finalize
deliberately skips the triage->todo move — so a card that just became executable
emits only a bare task:updated. None of the scheduler's existing event wakes
(task:created, globalPause/enginePaused unpause, per-task unpause) fire for that
transition, so the operator paid one poll interval for planning to start and
another for execution to start.

Track ids seen with status "planning" and trigger a scheduling pass when they
return to a dispatchable state, mirroring the pausedTaskIds unpause tracker.
Guarded on !status, not paused/userPaused, and a schedulable column, so a
planning -> failed/awaiting-approval park does not trigger a pointless pass.
schedule()'s re-entrance guard drops the call if a pass is already in flight,
and the id is cleared on task:deleted alongside the other per-task sets.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-07-25 09:27:26 -07:00
gsxdsm
b5578448fa fix(workflow): start planning immediately when a task is started
Pressing Start on a Coding (Ideas) card only writes a column move — there is no
dispatch call in that path — so planning did not begin until the triage
processor's next timer tick, up to pollIntervalMs (15s default) later. The
"Started planning" toast was optimistic and the card just sat in Todo.

- Wake planning discovery on the store's task:updated/task:created event when a
  task lands in todo/triage. Binding the wake to the store event rather than the
  Start button covers every move surface (board drag, context menu, task detail,
  List view, CLI, agent tools, POST /tasks/:id/move) by construction. The wake is
  advisory: it only advances WHEN the poll runs, so every pause, seed-prompt,
  dependency, and concurrency gate still applies.
- Admit a todo task whose PROMPT.md is missing instead of dropping it through a
  silent `catch {}`. The scheduler KEEPS a candidate whose prompt it cannot read,
  so such a card was invisible to planning while still visible to dispatch, with
  no log line in either lane. Unreadable (non-ENOENT) prompts now log.
- Route the scheduler's dispatch filter through the shared isUnplannedSeedPrompt
  predicate. Its open-coded strict bootstrap compare disagreed with triage on the
  refinement-seed shape, leaving hold-release as the only thing between an
  executor and a prompt containing just the operator's feedback text. The
  predicate also normalizes line endings/trailing whitespace, so a CRLF or
  trailing-newline round-trip no longer reclassifies an unplanned card as planned.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-07-25 09:14:11 -07:00
gsxdsm
0056d75314 FN-8569: surface unrecoverable report health
Classify parked direct reports as operator-actionable even when their stored state appears live.

- Add a reusable Reports Health classifier that prioritizes pause markers.
- Clear stale pause markers during live-state resumes without removing diagnostic errors.
- Cover desynchronized report states and document the health invariant.

Files changed:
 .../fn-8569-reports-health-error-unrecoverable.md  |  7 +++
 docs/architecture.md                               |  1 +
 .../agent-store-pause-marker-clear.test.ts         | 65 +++++++++++++++++++
 packages/core/src/agent-store.ts                   | 12 ++++
 .../src/__tests__/heartbeat-executor.test.ts       | 27 ++++++--
 .../engine/src/__tests__/reports-health.test.ts    | 73 ++++++++++++++++++++++
 packages/engine/src/agent-heartbeat.ts             | 36 ++++++-----
 packages/engine/src/index.ts                       |  6 ++
 packages/engine/src/reports-health.ts              | 70 +++++++++++++++++++++
 9 files changed, 278 insertions(+), 19 deletions(-)

Fusion-Task-Id: FN-8569

Fusion-Task-Lineage: 37c798a0-1f2b-4221-a0c6-ccbff8d72696

Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
2026-07-24 23:57:49 -07:00
gsxdsm
58d55d6439 chore(release): v0.74.0-beta.0
Version bump via changesets.
2026-07-24 23:12:53 -07:00
gsxdsm
1847d2bc2a chore(release): back-merge v0.73.0 from release
Keeps .changeset/project-switch-modal-reset.md: it was re-edited on main after
the v0.73.0 cut (f9f283293) and covers an unreleased fix.
2026-07-24 22:55:17 -07:00
gsxdsm
a727e20843 fix(auth): read the Anthropic credential preference from the resolved global dir
Global settings live in settings.json under the resolved global dir, which
falls back to the pre-rename ~/.pi/fusion and ~/.pi/kb dirs for installs that
never migrated. The reader hardcoded ~/.fusion, so on those installs the
operator's preference was silently ignored and resolution fell back to raw-key
precedence -- the same silent fallback the preference exists to remove.

Mirror the legacy-aware lookup getModelRegistryModelsPath already does for
models.json rather than importing core's resolver, which throws under VITEST
when called without an explicit dir.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-07-24 22:38:15 -07:00
gsxdsm
190cc041bc fix(lanes): resolve a model pair everywhere a session is constructed
Audit of the class behind the planning bug: createFnAgent forwards no model
unless both defaultProvider and defaultModelId are set, after which
pi-coding-agent picks its own built-in default (anthropic/claude-opus-4-8).
Seven lanes resolved no pair at all, so they hit that path on every call --
a permanent 401 invalid x-api-key for custom-provider and subscription
operators, and a hole in test-mode forcing:

- milestone/slice interviews (no model plumbing at all)
- subtask breakdown, triage and streaming paths
- agent generation
- text refine and goal drafting
- agent reflection (optional ctor pair no production caller supplies)

Two more resolved the halves independently, which the runtime treats as
unset: research synthesis defaults and pr-conflict-resolver's hand-rolled
copy of resolveProjectDefaultModel (which also skipped test-mode overrides).

Add lane-session-model.ts as the shared resolver and a source ratchet that
fails when a dashboard session is constructed from an inline literal with no
model decision.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-07-24 22:32:49 -07:00
gsxdsm
8dc6598aa0 feat(auth): let operators choose which Anthropic credential wins
An operator can hold a raw Anthropic API key and a Claude subscription OAuth
login at once, and the raw key always won silently. A stale or revoked saved
key therefore shadowed a working subscription and failed every direct Anthropic
call with 401 invalid x-api-key, while both Settings cards still read Active.

Add the global anthropicAuthPreference setting ("api-key" default, preserving
the historical precedence, or "subscription"), read in resolveAnthropicRuntimeApiKey
straight from ~/.fusion/settings.json so it applies without a restart and needs
no settings plumbing through createFusionAuthStorage. Neither value removes a
source: with one credential configured, resolution reaches it either way.

Settings -> Authentication now names the credential in use on the two Anthropic
cards and renders the control, but only when both are actually connected.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-07-24 22:32:39 -07:00
gsxdsm
e3dba364d1 FN-8564: update bundled Pi runtime to 0.82.0
Update Fusion's matched Pi dependencies and compatibility coverage for version 0.82.0.

- Pin Pi AI and coding-agent packages to the exact 0.82.0 release pair.
- Refresh provider catalog, supplemental model, auth storage, and Droid thinking coverage.
- Add the published CLI patch changeset.

Files changed:
 .changeset/fn-8564-pi-082.md                       |   7 +
 packages/cli/package.json                          |   4 +-
 packages/cli/src/__tests__/package-config.test.ts  |   2 +-
 packages/core/package.json                         |   2 +-
 packages/dashboard/package.json                    |   2 +-
 ...ister-model-routes-kimi-k3-supplemental.test.ts |   6 +-
 packages/engine/package.json                       |   4 +-
 .../src/__tests__/provider-registration.test.ts    |   4 +-
 packages/engine/src/auth-storage.ts                |  11 +-
 packages/engine/src/pi.ts                          |   6 +
 packages/pi-claude-cli/package.json                |   8 +-
 .../src/thinking-config.ts                         |  10 +-
 pnpm-lock.yaml                                     | 176 +++++++++++----------
 pnpm-workspace.yaml                                |   6 +-
 .../__tests__/check-pi-versions-pinned.test.mjs    |   8 +-
 15 files changed, 142 insertions(+), 114 deletions(-)

Fusion-Task-Id: FN-8564

Fusion-Task-Lineage: 543c5e17-4cb2-446f-9a1c-ec7ec8b8117a

Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
2026-07-24 19:06:14 -07:00
gsxdsm
127b640b3f chore(release): v0.73.0
Version bump via changesets.
2026-07-23 22:43:43 -07:00
gsxdsm
593f38249c chore(release): v0.73.0-beta.6
Version bump via changesets.
2026-07-23 22:16:37 -07:00
gsxdsm
049c36c9ae fix(chat): narrate task-chat step numbers 1-based to match the card's step count
The proactive status narration printed the internal 0-based step index, so the
final step of a 13-step task announced "Starting Step 12" next to a card
showing "12/13". Display now uses index + 1 in both the engine builders and
the store-side updateStep narration; the 0-based tool/PROMPT.md contract is
unchanged.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-23 21:43:58 -07:00
gsxdsm
b007de5f94 fix(ci,tests): repair binary release pipeline and re-green the full suite
Binary Release (v0.73.0-beta.5 was fully red):
- bun compile: mark chromium-bidi external — playwright-core@1.60 (feature-video)
  optionally requires it and bun fails closed on unresolvable requires.
- Windows desktop EXE: quote -c.publish.channel=beta in release.yml; PowerShell
  tokenizes the bare flag into `-c` + a path and electron-builder ENOENTs on it.

Full suite (all 4 shards red from stale-test drift, no product bugs found):
- engine: align mock stores/assertions with atomic store.moveTaskIf dispatch
  (#2371), the fail-closed non-empty PROMPT.md artifact gate (#2390), oldest-
  first admission (FN-8453), alreadyClaimed graph routing (#2393), startStep
  step projection (#2403/FN-8464), structured retry presentation (FN-8503),
  provider-lane pause reasons (#2339), typed column-boundary entry (#2378),
  Type.Integer in CAS document schemas (#2375), bounded model-registry refresh.
- engine-no-blocking-shellout: re-pin 17 drifted allowlist line numbers and drop
  the stale REBASE_HEAD entry whose execSync was removed.
- core: schema-applier expectations track migrations 0033-0035 (96 tables) and
  the synthetic 0000 fixture gains workflow_work_items/mission_contract_assertions;
  work-item terminal state is "succeeded" post-#2378.

Known follow-up (not addressed here): self-healing starved-refinement escalation
bumps task.priority, which FN-8453 oldest-first admission no longer consults.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-23 21:43:00 -07:00
gsxdsm
26628b356b chore(release): v0.73.0-beta.5
Version bump via changesets.
2026-07-23 20:23:55 -07:00
gsxdsm
c1d37cb137 test(FN-8544): assert mission autopilot system actor on status updates
Align MissionAutopilot expectations with autonomy-audit attribution: updateMission
carries the system actor options, and autopilot_disabled is recorded on the
mutation instead of a separate logMissionEvent mirror.
2026-07-23 17:28:56 -07:00
gsxdsm
0307476f35 fix(tests): restore listTasks hot-path contract and PG harness fs isolation
- usage-limit-detector + provider-health-monitor: make three bare listTasks()
  callers explicit with { slim: true }, restoring the architecture-hot-paths
  contract (they only read scalar pause/column/model-provider fields).
- pg-test-harness beforeEach: wipe <rootDir>/.fusion/tasks after TRUNCATE ...
  RESTART IDENTITY so filesystem isolation matches the id reset; stale task
  dirs from prior tests no longer collide with reused IDs (fixes
  store-reservation-atomicity rollback assertions).
2026-07-23 17:27:59 -07:00
gsxdsm
dfb9ca6630 FN-8543: enforce bounded generated-fix remediation
Keep generated fix chains within their root retry budget and preserve intervention stops.

- Track retry counts and stop reasons on the canonical root feature.
- Persist project-isolated lineage stops across generated-fix removal and archive paths.
- Serialize stop recording with generated-fix admission and expose conflict-safe mission resume behavior.

Files changed:
 .changeset/fn-8543-bounded-fix-lineage.md          |   7 +
 docs/missions.md                                   |   6 +-
 .../__tests__/postgres/mission-store.pg.test.ts    |  72 +++++++
 packages/core/src/async-mission-store-queries.ts   |  91 ++++++++-
 packages/core/src/async-mission-store.ts           | 206 ++++++++++++++++++---
 packages/core/src/index.ts                         |   2 +-
 packages/core/src/mission-store.ts                 |  10 +
 packages/core/src/mission-types.ts                 |   7 +
 .../0035_fn_8543_mission_lineage_stop.sql          |  31 ++++
 packages/core/src/postgres/schema-applier.ts       |  18 +-
 packages/core/src/postgres/schema/project.ts       |  21 +++
 .../core/src/task-store/archive-lifecycle-2.ts     |  15 +-
 .../core/src/task-store/async-archive-lineage.ts   |  10 +-
 packages/dashboard/src/mission-routes.ts           |  14 +-
 packages/engine/src/mission-execution-loop.ts      |  30 ++-
 15 files changed, 495 insertions(+), 45 deletions(-)

Fusion-Task-Id: FN-8543

Fusion-Task-Lineage: 24c03d63-5914-4072-aa17-16862432fc78

Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
2026-07-23 16:12:44 -07:00
Victor Canô
0c085bf444 fix(engine): pushAfterMerge no longer strands approved merges when the remote diverges (#2407)
## Problem

With `pushAfterMerge` enabled (and `mergeStrategy` other than
`pull-request`), if `origin/<integration-branch>` advances externally
between the local squash-merge and the push, the divergence path opens a
clean-room `git pull --rebase` and an AI agent resolves and stages the
conflicts — but the flow could end there: no `git rebase --continue`, no
push, and no surfaced error.

Because finalize runs *before* the push, the task is already `done`, so
a reviewed, approved merge is silently left container-only, and every
subsequent merge on the project stalls the same way. Separately, an
abort mid-push (`MergeAbortedError`) was swallowed with only a
process-log warning — no task-log entry, no run-audit event.

## Change

- **Deterministic regression coverage** for the conflicting-divergence
path (real-git fixture) proving the rebase runs to completion and the
push lands (refs converge), plus abort/termination scenarios.
- **Recovery-branch safety net:** before the clean-room rebase starts,
the pre-rebase local squash is force-pushed to a per-task remote branch
`fusion/<task-id>-stranded`, so approved content is never container-only
— even across process death or abort. Deleted after a successful target
push; retained on failure/abort as the recovery source.
- **Never-silent outcomes:** every non-pushed outcome (failure or abort)
writes a durable task-log entry and a `push:origin` run-audit event. The
audit contract now documents `push:origin` as polymorphic (dashboard
Smart Push vs. automated post-merge push) and enumerates the automated
path's outcomes, including the new `"aborted"` shutdown case.
- **Cleanup hardening:** `isRebaseInProgress` now probes Git's
worktree-specific `rebase-merge`/`rebase-apply` state directories
(async, timeout-guarded) so a completed rebase can't receive a spurious
second `--continue`; unfinished rebases are cleaned up.

Out of scope by design: withholding the "merge confirmed" state until
the push succeeds — the `FNXC:MergePush` invariant ("a push problem can
never park or roll back a landed merge") is deliberate; the recovery
branch + surfacing satisfy the data-preservation intent without breaking
it.

## Files
`packages/engine/src/merger-ai.ts`, `packages/engine/src/merger.ts`,
`packages/engine/src/run-audit.ts`, new/updated tests under
`packages/engine/src/__tests__/`, `docs/settings-reference.md`,
`docs/dashboard-guide.md`, `AGENTS.md`, and a labeled changeset.

## Validation
`tsc --noEmit` clean; engine divergence + merger suites pass (41 tests);
rebased onto current `main` with no conflicts.

---

_Developed with Claude Code, under human supervision and review._


<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit

* **Bug Fixes**
* Prevented approved post-merge pushes from becoming stranded when the
remote diverges by using a recovery-branch workflow and safer cleanup.
* Improved behavior and reporting when pushes are aborted or fail after
merge, including clearer non-fatal status and audit outcomes.
* **Documentation**
* Expanded push-after-merge and dashboard Smart Push documentation with
recovery-branch and `push:origin`/`push:recovery-branch` outcome
semantics.
* **Tests**
* Added end-to-end regression tests for divergent/conflicting AI
push-after-merge flows, including abort and worktree cleanup
verification.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->

---------

Co-authored-by: Victor Cano <victortroz@gmail.com>
Co-authored-by: Claude <noreply@anthropic.com>
2026-07-23 15:55:03 -07:00
gsxdsm
e5caea542a FN-8544: gate mission remediation behind autopilot
Keep mission validation report-only until an operator explicitly enables autopilot.

- Gate validator-created remediation features and task dispatch behind mission autopilot.
- Audit attributed status and autopilot transitions atomically across mission stores.
- Expose mission autonomy controls and document the opt-in lifecycle.

Files changed:
 .changeset/fn-8544-mission-autonomy-audit.md       |   7 ++
 docs/missions.md                                   |  10 +-
 packages/cli/src/extension.ts                      |  26 ++++-
 .../__tests__/postgres/mission-store.pg.test.ts    |  27 +++++
 packages/core/src/async-mission-store.ts           |  70 +++++++++++--
 packages/core/src/index.gate.ts                    |   3 +
 packages/core/src/index.ts                         |   3 +
 packages/core/src/mission-store.ts                 | 113 +++++++++++++--------
 packages/core/src/mission-types.ts                 |  22 ++++
 .../dashboard/app/components/MissionManager.tsx    |   1 +
 packages/dashboard/src/mission-routes.ts           |  25 +++--
 .../src/__tests__/agent-mission-tools.test.ts      |  17 +++-
 .../src/__tests__/mission-execution-loop.test.ts   |  21 ++++
 packages/engine/src/agent-heartbeat.ts             |   4 +-
 packages/engine/src/agent-tools.ts                 |  30 +++++-
 packages/engine/src/executor.ts                    |   5 +-
 packages/engine/src/mission-autopilot.ts           |  51 +++++-----
 packages/engine/src/mission-execution-loop.ts      |  49 ++++++---
 packages/engine/src/triage.ts                      |   5 +-
 19 files changed, 377 insertions(+), 112 deletions(-)

Fusion-Task-Id: FN-8544

Fusion-Task-Lineage: 23a69923-5a19-407e-9fe2-8973c166ee9a

Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
2026-07-23 15:25:09 -07:00
gsxdsm
e7c9b2ac80 FN-8542: separate milestone acceptance from feature validation
Keep feature validation scoped to linked feature assertions while evaluating milestone acceptance at rollup.

- Add assertion scope and origin provenance with database migration support
- Synchronize milestone acceptance prose to a canonical milestone assertion
- Prevent milestone assertions from affecting feature verdicts and document the completion contract
- Cover scoped validation and milestone rollup behavior with core and engine tests

Files changed:
 .changeset/fn-8542-feature-validation-scope.md     |   7 +
 docs/missions-completion-contract.md               |  25 +-
 docs/missions.md                                   |   6 +-
 .../mission-store.sync-loop-transition.test.ts     |  53 +++++
 packages/core/src/async-mission-store-queries.ts   |  17 +-
 packages/core/src/async-mission-store.ts           |  58 ++++-
 packages/core/src/mission-store.ts                 | 118 +++++++++-
 packages/core/src/mission-types.ts                 |  25 ++
 .../core/src/postgres/migrations/0000_initial.sql  |   2 +
 .../0034_milestone_assertion_provenance.sql        |  30 +++
 packages/core/src/postgres/schema-applier.ts       |  24 +-
 packages/core/src/postgres/schema/project.ts       |   1 +
 .../src/__tests__/mission-execution-loop.test.ts   | 161 ++++++++++++-
 packages/engine/src/mission-execution-loop.ts      | 262 ++++++++++++++++-----
 14 files changed, 700 insertions(+), 89 deletions(-)

Fusion-Task-Id: FN-8542

Fusion-Task-Lineage: cf112d31-376a-456e-be08-225eab5de393

Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
2026-07-23 14:39:44 -07:00
gsxdsm
2978ec4973 FN-8551: add accessible agent heartbeat controls
Add per-agent and project-wide heartbeat enablement controls.

- Add preserved runtime configuration helpers and heartbeat controls across agent list, board, and org-chart views.
- Add bulk enable/disable actions, accessible labels, localized copy, documentation, and release metadata.
- Cover heartbeat mutations through dashboard, route, scheduler, and layout smoke tests.

Files changed:
 .changeset/fn-8551-agent-heartbeat-controls.md     |   7 +
 docs/dashboard-guide.md                            |   2 +
 packages/dashboard/app/api/agents.ts               |  12 ++
 packages/dashboard/app/api/legacy.ts               |   2 +
 .../dashboard/app/components/AgentDetailView.tsx   |  14 +-
 packages/dashboard/app/components/AgentsView.css   |  11 ++
 packages/dashboard/app/components/AgentsView.tsx   | 186 ++++++++++++++++-----
 .../AgentDetailView.advanced-settings.test.tsx     |  29 ++++
 .../__tests__/AgentDetailView.test-helpers.ts      |   2 +
 .../app/components/__tests__/AgentsView.test.tsx   |  68 ++++++++
 .../dashboard/scripts/browser-layout-smoke.mjs     |  54 ++++++
 .../src/routes/__tests__/agent-core-routes.test.ts |  69 ++++++++
 .../src/__tests__/heartbeat-scheduler.test.ts      |  21 +++
 packages/i18n/locales/en/app.json                  |  19 ++-
 packages/i18n/locales/es/app.json                  |  19 ++-
 packages/i18n/locales/fr/app.json                  |  19 ++-
 16 files changed, 484 insertions(+), 50 deletions(-)

Fusion-Task-Id: FN-8551

Fusion-Task-Lineage: 2d4c8a15-a444-47b6-b57c-f41fc4163155

Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
2026-07-23 14:32:58 -07:00
gsxdsm
3d0ce2ed3a FN-8545: enforce mission lineage task admission
Protect feature bootstrap task creation with mission-lineage admission rules.

- Restrict supervised agents to linked autonomous mission features
- Atomically claim defined feature tasks and preserve duplicate ownership
- Lock defined feature claims to prevent concurrent bootstrap inserts
- Document admission behavior and add PostgreSQL and delegation coverage

Files changed:
 .changeset/fn-8545-mission-admission.md            |   7 +
 docs/agents.md                                     |   6 +
 docs/missions.md                                   |  12 +-
 .../__tests__/postgres/mission-store.pg.test.ts    | 119 ++++++++++++
 packages/core/src/async-mission-store.ts           | 203 +++++++++++++++++++--
 packages/core/src/duplicate-guard.ts               |  14 +-
 packages/core/src/mission-store.ts                 |  15 ++
 packages/core/src/task-store/task-creation.ts      | 152 ++++++++++-----
 .../src/__tests__/agent-tools-delegation.test.ts   | 152 ++++++++++++++-
 packages/engine/src/agent-tools.ts                 | 151 ++++++++++++++-
 10 files changed, 753 insertions(+), 78 deletions(-)

Fusion-Task-Id: FN-8545

Fusion-Task-Lineage: c6e1b46d-f434-4b07-93bc-27e1f6b491b1

Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
2026-07-23 14:21:29 -07:00
gsxdsm
e734ed8a48 FN-8541: expose actionable validator diagnostics
Surface normalized, secret-safe validator evidence and assertion verdicts across mission events and remediation work.

- Normalize, redact, bound, and render per-assertion validation diagnostics in shared core APIs.
- Attach verdict-driven diagnostics to failure events and generated fixes while suppressing duplicate triage noise.
- Display validator evidence in Mission activity and document the operator workflow.
- Add core and engine coverage for evidence, mixed blocked verdicts, and triage behavior.

Files changed:
 .changeset/fn-8541-validator-diagnostics.md        |   7 ++
 docs/missions.md                                   |  17 +++
 .../mission-store.validation-diagnostics.test.ts   |  65 ++++++++++
 packages/core/src/async-mission-store.ts           |   8 +-
 packages/core/src/index.gate.ts                    |  10 ++
 packages/core/src/index.ts                         |  10 ++
 packages/core/src/mission-store.ts                 |  10 +-
 packages/core/src/mission-types.ts                 | 136 +++++++++++++++++++++
 .../dashboard/app/components/MissionManager.css    |  21 ++++
 .../dashboard/app/components/MissionManager.tsx    |  56 +++++++++
 packages/dashboard/app/components/mission-types.ts |   3 +
 .../src/__tests__/mission-execution-loop.test.ts   |  44 +++++++
 .../mission-validator-behavioral-posture.test.ts   |  37 +++++-
 packages/engine/src/mission-execution-loop.ts      | 117 +++++++++++++-----
 14 files changed, 501 insertions(+), 40 deletions(-)

Fusion-Task-Id: FN-8541

Fusion-Task-Lineage: b226fc34-f35c-4ce5-bcb5-e418529caaa7

Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
2026-07-23 13:21:10 -07:00
gsxdsm
2021d5679b FN-8540: render mission hierarchies in agent lookup
Render complete mission hierarchy details from the fn_mission_show agent tool.

- Format mission, linked-goal, milestone, slice, and feature metadata with IDs and statuses
- Link features to their tasks and bound verbose acceptance and verification text
- Cover populated and empty hierarchy responses with regression tests
- Add a patch changeset for the agent lookup fix

Files changed:
 .changeset/fn-8540-mission-show-hierarchy.md       |  7 ++
 .../src/__tests__/agent-mission-tools.test.ts      | 68 +++++++++++++++++++
 packages/engine/src/agent-tools.ts                 | 77 +++++++++++++++++++++-
 3 files changed, 150 insertions(+), 2 deletions(-)

Fusion-Task-Id: FN-8540

Fusion-Task-Lineage: f2282226-3f4e-4d9f-bd8e-d18ad9639c03

Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
2026-07-23 12:46:09 -07:00
gsxdsm
84d7306bd3 FN-8546: fix ideation candidate discovery
Expose persisted ideation candidate identities so agents can select a candidate for convergence.

- Render candidate IDs, provenance, source references, and content in show and diverge responses.
- Cover discover-to-converge behavior, duplicate content, empty sessions, and missing sessions.
- Document the direct candidate-ID convergence workflow and add a patch changeset.

Files changed:
 .changeset/fn-8546-ideation-candidate-ids.md       |  7 ++
 docs/agent-tool-surface-full-loop.md               |  4 +-
 docs/ideation/persisted-diverge-converge.md        |  6 +-
 .../src/__tests__/agent-ideation-tools.test.ts     | 93 +++++++++++++++++++---
 packages/engine/src/agent-tools.ts                 | 23 +++++-
 5 files changed, 116 insertions(+), 17 deletions(-)

Fusion-Task-Id: FN-8546

Fusion-Task-Lineage: 7232701f-5c6b-40fc-8d46-d65b97af9c0a

Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
2026-07-23 12:26:02 -07:00
gsxdsm
2499803c73 fix: guard CE sessions against concurrent-turn displacement and harden JSON-protocol parsing
Port the planning turn-admission invariant (FNXC:PlanningTurnAdmission,
2026-07-22) into the Compound Engineering orchestrator: at most one turn
(opening/answer/resume-rehydration) is admitted per CE session, reserved
synchronously and held until the turn settles — a re-entered mobile view
re-submitting a turn now gets CeTurnInProgressError (HTTP 409) instead of
displacing the in-flight turn's live agent, which surfaced as "Failed to
parse agent response: AI returned no valid JSON". cancel()/discard()
force-clear the reservation; releases are token-scoped so a stale release
can't drop a newer turn's slot.

In the engine interactive-ai-session seam: bump the reformat retry from
one to two attempts (non-Anthropic default models comply less reliably
with the JSON-only protocol), and log every failed parse with a bounded
raw-response snippet plus resolved provider/model — including a distinct
empty-assistant-message marker — so support can diagnose these reports
without a repro.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-23 10:31:22 -07:00
gsxdsm
a45d82d09b chore(release): v0.73.0-beta.4
Version bump via changesets.
2026-07-23 00:16:34 -07:00
Fusion
96a9da7979 FN-8505: notify operators of terminal task wedges
Deliver durable, actionable notifications when terminal task recovery wedges.

- Persist and deduplicate terminal wedge notification episodes across task updates and service restarts.
- Classify terminal failure and self-healing escalation states, then deliver actionable ntfy and mailbox alerts.
- Align PostgreSQL baseline and upgrade migration registration for the durable wedge field.

Files changed:
 .changeset/fn-8505-task-wedge-notifications.md     |   7 +
 docs/agents.md                                     |   4 +
 docs/architecture.md                               |   4 +
 .../core/src/postgres/migrations/0000_initial.sql  |   2 +
 .../migrations/0033_fn-8505_wedge_notification.sql |   5 +
 packages/core/src/postgres/schema-applier.ts       |  29 +++-
 packages/core/src/postgres/schema/project.ts       |   1 +
 packages/core/src/store.ts                         |  22 ++-
 packages/core/src/task-store/persistence.ts        |   2 +
 packages/core/src/task-store/serialization.ts      |   1 +
 packages/core/src/task-store/task-row-mappers.ts   |   2 +-
 packages/core/src/task-store/task-update.ts        |   5 +
 packages/core/src/types.ts                         |  14 ++
 packages/core/src/types/workflow-steps.ts          |   2 +
 .../src/__tests__/notification-service.test.ts     |  20 +++
 packages/engine/src/__tests__/notifier.test.ts     |  26 +++-
 packages/engine/src/__tests__/self-healing.test.ts |   9 +-
 .../__tests__/notification-service.test.ts         |  34 ++++-
 .../__tests__/task-wedge-notification.test.ts      | 134 +++++++++++++++++
 .../src/notification/notification-service.ts       | 108 +++++++++++++-
 packages/engine/src/notification/ntfy-provider.ts  |  11 ++
 .../src/notification/task-wedge-notification.ts    | 160 +++++++++++++++++++++
 packages/engine/src/notifier.ts                    |   3 +
 packages/engine/src/self-healing.ts                |  17 +++
 24 files changed, 610 insertions(+), 12 deletions(-)

Fusion-Task-Id: FN-8505
Fusion-Task-Lineage: eee85220-18ba-475d-9d01-dc96e2b923e6
Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
2026-07-22 20:07:48 -07:00
gsxdsm
2cbb80c501 chore(release): v0.73.0-beta.3
Version bump via changesets.
2026-07-22 19:37:44 -07:00
gsxdsm
227281dc32 FN-8503: preserve unbounded Code Review retries
Keep Code Review remediation retry policies accurate across graph execution and recovery.

- Preserve unlimited retry presentation when Code Review has no configured cap
- Enforce finite Code Review caps during failed-step recovery
- Validate non-negative revision settings and document the active retry policy

Files changed:
 .../fn-8503-unbounded-code-review-retries.md       |   7 ++
 docs/workflow-steps.md                             |   2 +-
 .../core/src/__tests__/builtin-workflows.test.ts   |   8 +-
 packages/core/src/builtin-workflow-settings.ts     |   4 +
 .../workflow-graph-optional-step-fix.test.ts       | 135 +++++++++++++++++++++
 packages/engine/src/executor.ts                    |  51 ++++++--
 6 files changed, 193 insertions(+), 14 deletions(-)

Fusion-Task-Id: FN-8503

Fusion-Task-Lineage: 7bd555d1-23e5-42ea-b6f5-0b9fe4da7f94

Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
2026-07-22 18:32:36 -07:00
gsxdsm
c5c8820e79 fix(engine): re-pin drifted git-plumbing lines in shellout static guard
FN-8490 + graph-owned-cutover follow-ups shifted lines above 5 allowlisted
deterministic git-plumbing execSync sites (self-healing.ts, executor.ts),
breaking the call-site file:line:signature match. Refresh the pinned lines
to restore the engine-no-blocking-shellout static guard signal. No
production behavior change; sites are unchanged legitimate git plumbing.
2026-07-22 17:16:52 -07:00
gsxdsm
1dd36ed4c6 fix(FN-8492): mark orphaned pending step results failed instead of deleting them
Code-review follow-up on 4413699de. Deleting an orphaned pending review
entry was a severity inversion: the merge gate blocks on pending/failed
results, not on an enabled step with NO result, so deletion silently
satisfied the gate and the task merged with its review skipped (verified
live: FN-8492 landed on main without Code Review re-running). Orphans are
now rewritten to status:"failed" — the gate stays closed and the
failed-pre-merge-steps recovery / FN-7720 operator-bypass paths own the
re-run decision.

Also from review: the sweep now runs in periodic maintenance too (a step
session can die without a restart), skips executor-owned in-progress rows
(resume is deferred ~30s at startup, so their liveness is unprovable when
startup recovery runs), re-reads the row immediately before the write so
the whole-array update cannot clobber a fresh lease, counts recovery on
the successful mutation rather than after the audit emit, and the new
audit event literal is registered in DatabaseMutationType (cast dropped).
Tests now cover all three liveness-triple legs, >500-row pagination,
in-progress skip, per-task write-failure isolation, and the never-delete
invariant; the needs-replan adoption row moved under a preserve-group
header.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-22 16:55:22 -07:00
gsxdsm
4413699de0 fix(FN-8492): clear orphaned pending workflow-step results at startup
An engine restart that kills an in-flight pre-merge step session (FN-8492's
Code Review) left its pending workflowStepResult behind with no live session.
The merge gate read it as incomplete pre-merge steps, surfaced an identical
stall every 30 minutes, and the deadlock disposer parked the task failed two
hours later. resolveOrphanedPendingStepResults existed for exactly this but
shipped with no caller (same U9 gap as the adoption table).

Wire it: a startup sweep right after legacy adoption clears pending results
whose task has no live session (activeSessionRegistry / executingTaskLock /
isTaskActive), emitting task:reconcile-orphaned-pending-step-results with
ids/counts-only metadata. User pauses and live resumed sessions are never
disturbed.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-22 16:05:59 -07:00
gsxdsm
3cd023fa43 FN-8491: add declarative plugin MCP server registrations
Enable plugins to declare per-project MCP server registrations.

- Add plugin MCP server contribution types, loading, and resolution across core and engine runtimes.
- Expose resolved plugin registrations through project configuration APIs and MCP settings UI.
- Document the declarative contribution API and add release metadata and regression coverage.

Files changed:
 .changeset/plugin-mcp-servers.md                   |  7 ++
 docs/PLUGIN_AUTHORING.md                           | 17 +++++
 docs/mcp.md                                        |  4 ++
 docs/settings-reference.md                         |  4 ++
 packages/core/src/__tests__/mcp-config.test.ts     | 33 +++++++++
 .../__tests__/plugin-contribution-types.test.ts    | 16 +++++
 .../__tests__/plugin-loader-single-load.test.ts    | 23 +++++++
 packages/core/src/index.gate.ts                    |  3 +
 packages/core/src/index.ts                         |  3 +
 packages/core/src/mcp-config.ts                    | 37 ++++++++--
 packages/core/src/plugin-loader.ts                 | 24 +++++++
 packages/core/src/plugin-mcp-servers.ts            | 78 ++++++++++++++++++++++
 packages/core/src/plugin-types.ts                  | 15 ++++-
 packages/core/src/types.ts                         |  2 +-
 .../__tests__/SettingsModal.mcp.test.tsx           | 34 +++++++++-
 .../settings/sections/McpServersCard.tsx           | 52 ++++++++++-----
 .../settings/sections/ProjectMcpSection.tsx        | 31 ++++++++-
 .../register-config-mcp-pi-settings-routes.test.ts | 18 ++++-
 packages/dashboard/src/routes/context.ts           | 71 +++++++++++++++++++-
 .../register-config-mcp-pi-settings-routes.ts      | 30 ++++++++-
 .../engine/src/__tests__/mcp-resolution.test.ts    | 20 ++++++
 packages/engine/src/mcp-resolution.ts              | 15 ++++-
 packages/engine/src/plugin-runner.ts               | 38 +++++++++++
 packages/engine/src/runtimes/in-process-runtime.ts | 23 +++++++
 packages/plugin-sdk/src/index.ts                   |  1 +
 25 files changed, 563 insertions(+), 36 deletions(-)

Fusion-Task-Id: FN-8491

Fusion-Task-Lineage: be7e22fa-5776-4b3d-9fd1-a873799e6427

Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
2026-07-22 15:25:20 -07:00
gsxdsm
53e3063e9f FN-8490: load skills for foreach step-execute sessions
Honor skill-executor configuration for implementation sessions created by foreach templates.

- Propagate validated step-execute skill names through workflow seam context.
- Load namespaced and bare skills with configured discovery paths for pinned step sessions.
- Add regression coverage, workflow documentation, and a minor changeset.

Files changed:
 .changeset/fn-8490-step-execute-skill.md           |   7 ++
 docs/workflow-steps.md                             |   4 +-
 .../__tests__/step-execute-skill-loading.test.ts   | 128 +++++++++++++++++++++
 packages/engine/src/executor.ts                    |  62 +++++++++-
 packages/engine/src/workflow-node-handlers.ts      |  21 ++++
 5 files changed, 219 insertions(+), 3 deletions(-)

Fusion-Task-Id: FN-8490

Fusion-Task-Lineage: aa1ff02d-3139-45f2-8853-f53c0aef0f2f

Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
2026-07-22 15:07:59 -07:00
gsxdsm
242b22dbf4 FN-8488: add branded icons for first-class providers
Ensure every first-class authentication provider renders an intentional branded icon.

- Add custom llama.cpp, Brave Search, and Tavily provider icons.
- Make OpenClaw theme-aware and add stable icon test selectors.
- Export the API-key provider catalog and verify dashboard parity.

Files changed:
 packages/dashboard/app/components/ProviderIcon.tsx | 65 ++++++++++++++++---
 .../app/components/__tests__/ProviderIcon.test.tsx | 73 +++++++++++++++++++++-
 packages/dashboard/app/styles.css                  |  6 ++
 .../__tests__/auth-provider-catalog-parity.test.ts | 11 ++++
 packages/engine/src/index.ts                       |  1 +
 packages/engine/src/provider-auth.ts               |  6 +-
 6 files changed, 148 insertions(+), 14 deletions(-)

Fusion-Task-Id: FN-8488

Fusion-Task-Lineage: e75e823b-4f03-4ad9-892f-100212a6022e

Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
2026-07-22 14:42:04 -07:00
gsxdsm
5de244400f feat(engine): defer grok-cli fallback to the Grok CLI runtime on primary failure
Instead of dropping a fallback-only grok-cli pair when no GROK_API_KEY is
Fusion-visible, defer it: the session runs the configured primary, and on
the first retryable model-selection failure it creates a session on the
Grok CLI runtime with the fallback model and re-issues the failed prompt
there; later prompts stay on the swapped session. Engagement reports
through onFallbackModelUsed and an ids-only
session:grok-cli-fallback-engaged run-audit event. The pair is dropped
(with audit flag) only when the Grok runtime plugin is unavailable.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-22 14:28:02 -07:00
gsxdsm
d36059bdce fix(engine): stop grok-cli fallback preempting the configured primary model
A configured grok-cli fallback with no Fusion-visible GROK_API_KEY was
promoted to primary at session start (FN-7758 seam), so every planning
session silently ran grok-4.5 instead of the configured planning model.
The no-visible-key Grok CLI auto-route now fires only for a grok-cli
primary; a fallback-only grok-cli pair is dropped with a warning and an
audited grokCliFallbackDropped flag, and session:runtime-resolved now
records the post-transform model pair the session actually runs.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-22 14:14:36 -07:00
gsxdsm
56efd7488e fix(engine): stop false-positive stuck loop kills on iterative work (#2404)
## Summary

- Fix a false-positive in `StuckTaskDetector` where legitimate long
single-step work (E2E debugging, iterative fix/test cycles) was
classified as a loop and kill/requeued.
- Root cause: loop meant “no step status transition for
`taskStuckTimeoutMs` + high activity volume,” conflating **step
progress** with **actual activity**. Agents can stay productively busy
on one step for 10+ minutes with zero repetition.
- Loop now requires thrash evidence on top of volume + no step progress:
- **repetitive tool fingerprints** (`toolName` + primary-arg detail in a
sliding window), or
  - **elevated ignored step-update rebuffs** (≥ 10)
- Wire tool name/detail from `AgentLogger` → executor / step-session
into `recordActivity(...)` so novelty is measurable.
- Document the thrash-evidence rule in `docs/architecture.md`.

## Test plan

- [x] `pnpm --filter @fusion/engine exec vitest run
src/__tests__/stuck-task-detector.test.ts
src/__tests__/reliability-interactions/non-progress-churn.test.ts`
- [x] Regression: high-volume **diverse** iterative activity (174
events) does **not** classify as loop
- [x] High bare text/heartbeat volume without tools does **not**
classify as loop
- [x] Repetitive identical tool fingerprint + timeout **does** classify
as loop
- [x] Ignored step-update thrash (≥10) with volume **does** classify as
loop
- [x] Existing FN-5168 no-progress-churn + FN-6598 verification
suppression paths still pass
- [ ] CI gate green

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit

* **Bug Fixes**
* Improved stuck/loop classification by requiring explicit “thrash
evidence” (repetitive tool fingerprints and/or elevated ignored progress
rebuffs), reducing false positives for busy but diverse work.
* Updated loop evidence tracking to incorporate tool name plus
summarized tool-argument detail.
* Cleared loop evidence appropriately after verification, progress
updates, and task resumption.
* Extended tool-start telemetry/callbacks to include optional tool
detail.
* **Documentation**
* Refined loop-classification criteria to match the new evidence gates.
* **Tests**
* Updated/expanded stuck/loop and churn scenarios to validate the
evidence-based behavior and callback ordering.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2026-07-22 13:43:55 -07:00
gsxdsm
f63047871c fix: allow freeform chat task create without mission lineage (#2406)
## Summary

Chat-driven `fn_task_create` rejected freeform intake with `Approved
mission_lineage is required` even though the tool schema marks
`mission_lineage` as optional. That was FN-8307 mission admission
over-applied beyond autonomous heartbeat patrol.

This restores freeform chat/board-equivalent creates while keeping
idle-heartbeat mission-lineage enforcement.

## What changed

- **`fn_task_create` / `fn_delegate_task`**: omit `mission_lineage`
succeeds for user-directed surfaces; hard-require only when the tool is
registered with `requireMissionLineage` (idle heartbeat patrol).
- **Gates**: missing lineage is policy-governed (`allow` /
`require-approval` / `block`) instead of a hard pre-block, so
permanent-agent chat can create freeform tasks under normal policy.
- **Heartbeat no-task delegate**: also sets `requireMissionLineage:
true` so freeform off-mission work cannot bypass admission via
`fn_delegate_task`.
- Supplied lineage is still fully validated (Feature → Slice → Milestone
→ Mission) on every surface.
- Parent inheritance still applies when not in require mode.

## Test plan

- [x] Unit: freeform `fn_task_create` without lineage creates a task
with no `missionId`/`sliceId`
- [x] Unit: freeform `fn_delegate_task` without lineage succeeds
- [x] Unit: `requireMissionLineage: true` still hard-fails without
lineage
- [x] Unit: gates treat missing lineage as policy disposition, not hard
block
- [ ] CI gate green

## Symptom

**Original:** chat tool call `{ description: "Create a red button",
priority: "high" }` → `ERROR: Approved mission_lineage is required; no
task was created.`

**Expected after fix:** task is created freeform without mission fields.


<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->

## Summary by CodeRabbit

- **Bug Fixes**
- Freeform chat task creation and delegation can now proceed without
`mission_lineage`.
- Permission policies continue to govern these actions, including
approval requirements.
- Autonomous idle patrols still require approved mission lineage before
creating or delegating tasks.
- Task creation no longer receives mission-specific metadata when no
lineage is provided.

- **Tests**
- Expanded coverage for freeform and mission-linked task creation,
delegation, and policy-gating scenarios.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2026-07-22 13:36:23 -07:00
gsxdsm
d194290a75 fix(engine): reject out-of-order step starts (#2403)
## Summary

Ordered task steps can no longer appear active ahead of unfinished
predecessors. Step starts now use the same dependency-aware ordering
guard as completions, while steps explicitly declared independent remain
parallelizable. Rejected executor updates explain that the lifecycle
transition was suppressed instead of implying completed work was
overwritten.

## Validation

- Reproduced the FN-8490 concurrent update sequence and verified later
steps remain pending.
- Passed 15 PostgreSQL step-order tests, the focused executor response
test, core and engine typechecks, changeset validation, and `pnpm
verify:fast` including boot smoke.
- The full `executor-prompt.test.ts` run retains five pause-behavior
expectation failures that reproduce unchanged on `origin/main`.


<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit

* **New Features**
* Enhanced the step start hook to support an awaited “pre-start
projection” that can reject startup via `false` (sync or async),
preventing step-session creation/completion.
* Added a step-start “verdict” so steps can be started or blocked
deterministically (including “resumed” behavior).
* **Bug Fixes**
* Prevented ordered/dependency steps from transitioning out-of-order by
enforcing guards for both in-progress and done transitions, including
concurrent update attempts.
* Improved integrity/out-of-order warning behavior and suppression
details when persisted status doesn’t match expectations.
* **Tests**
* Added/updated PostgreSQL and engine regression coverage for
blocked/resumed start and start-rejection control flow.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2026-07-22 11:02:57 -07:00