The Anthropic OAuth refresh request sent `scope: user:profile`, which under
RFC 6749 §6 re-issues the access token with exactly that scope — stripping
`user:inference` and 403-ing every model call while the account still read
as "logged in via OAuth". Stop sending `scope` on refresh (Anthropic then
preserves the originally-granted scopes, matching pi-ai), and widen
ANTHROPIC_DEFAULT_SCOPES to mirror pi-ai's full granted Claude Code scope
set so any fallback describes a usable token.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Reorders task-definition prompt templates so the Before -> After Transformation section appears before other sections, making the expected change visible first.
- Move the Before -> After Transformation section ahead of other sections in agent-prompts.ts task-definition templates
- Update docs/task-management.md to reflect the new section order
- Add/extend tests in agent-prompts.test.ts and triage.test.ts covering the new ordering
- Add changeset fn-7593-before-after-top.md documenting the change
Files changed:
.changeset/fn-7593-before-after-top.md | 7 +++++++
docs/task-management.md | 2 +-
packages/core/src/__tests__/agent-prompts.test.ts | 20 ++++++++++++++++++++
packages/core/src/agent-prompts.ts | 20 +++++++++++++-------
packages/engine/src/__tests__/triage.test.ts | 17 +++++++++++++++++
5 files changed, 58 insertions(+), 8 deletions(-)
Fusion-Task-Id: FN-7593
Fusion-Task-Lineage: d0d5eb4d-2fe0-456c-b061-5c078b78911b
Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
The Plan Review pre-merge gate could loop a task through triage↔plan-review
indefinitely (FN-7525 ran 13+ replans overnight with no operator visibility),
and its reviewer frequently produced "no PROMPT.md found / data lives in a DB"
non-verdicts that fed the loop.
Root cause of the non-verdicts: the reviewer runs readonly with cwd set to the
task worktree, but the spec lives at project-root .fusion/tasks/<id>/PROMPT.md —
outside the worktree — so telling it to "Read PROMPT.md" had it search the wrong
tree and give up. Four fixes:
1. Inject the PROMPT.md content (via readTaskArtifact, store-backed) directly
into the Plan Review reviewer prompt so the verdict never depends on the
agent locating the file.
2. Self-retry a malformed reviewer response once on the primary model when no
fallback model is configured, so a single fumbled response gets a second
chance instead of feeding the replan loop.
3. A malformed (advisory_failure, no parsed verdict) plan-review result can
never trigger a triage replan — it is an infra failure, not a plan defect.
4. Cap the unbounded plan-review replan default at 15 attempts; past the cap it
emits a loud halting log entry and leaves the task for a human instead of
looping forever. Explicit numeric operator budgets are unchanged.
Tests: cap halts at 15 / still replans at 14 / malformed never replans. Existing
Plan Review replan and malformed-verdict-gate tests still pass.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Add workflow nodes for mid-flow user reach-out and early exit from a workflow run.
- Add `ask-user` IR node kind that reuses the await-input park/resume mechanism and surfaces the question in the task chat for brainstorming/clarification.
- Add `exit-gate` IR node kind that terminates the workflow early, with an optional condition.
- Wire both node kinds through the engine executor and workflow-node-handlers, including a new exit-gate-runner.
- Update the WorkflowNodeEditor palette, node summaries, and node help text for the two new node types.
- Extend workflow-flow-mapping to support the new node kinds.
- Keep `prompt`+`awaitInput` as a back-compat alias.
- Add core/engine/dashboard tests covering the new node kinds.
- Document the new nodes in docs/workflow-steps.md.
- Add changeset for the new minor feature.
Files changed:
.changeset/fn-7579-ask-user-exit-gate-nodes.md | 7 +
docs/workflow-steps.md | 28 ++++
packages/core/src/__tests__/workflow-ir.test.ts | 120 ++++++++++++++
packages/core/src/workflow-ir-types.ts | 12 +-
packages/core/src/workflow-ir.ts | 47 ++++++
.../app/components/WorkflowNodeEditor.tsx | 181 ++++++++++++++++++++-
.../app/components/__tests__/node-summary.test.ts | 43 +++++
.../__tests__/workflow-flow-mapping.test.ts | 49 ++++++
.../app/components/nodes/WorkflowNodeTypes.tsx | 14 +-
.../dashboard/app/components/nodes/node-help.ts | 24 +++
.../dashboard/app/components/nodes/node-summary.ts | 28 ++++
.../app/components/workflow-flow-mapping.ts | 4 +
.../workflow-graph-executor-handlers.test.ts | 115 +++++++++++++
.../src/__tests__/workflow-node-handlers.test.ts | 66 ++++++++
packages/engine/src/executor.ts | 23 ++-
packages/engine/src/workflow-node-handlers.ts | 18 +-
.../src/workflow-node-runners/exit-gate-runner.ts | 81 +++++++++
17 files changed, 849 insertions(+), 11 deletions(-)
Fusion-Task-Id: FN-7579
Fusion-Task-Lineage: 9a89ff49-200d-4a6c-b97c-15d219349ee5
Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
Extends FN-7554's single-repo PR revert path to workspace (multi-repo) tasks: when autoMerge is disabled, the revert route now opens one dedicated fusion/revert-<id> PR per sub-repo instead of refusing workspace tasks outright.
- Add prepareWorkspaceRevertPrBranches (packages/engine/src/task-revert.ts): classifies every sub-repo first and only prepares a per-sub-repo fusion/revert-<id> branch when all sub-repos are clean/already-reverted (all-or-nothing at branch-prep phase); never force-writes any sub-repo integration branch.
- Export the new helper from packages/engine/src/index.ts.
- Extend POST /api/tasks/:id/revert (register-task-workflow-routes.ts) to resolve owner/repo and check the GitHub rate limiter for every sub-repo before pushing/creating any PR, opening one PR per sub-repo and returning an additive { mode: "pr", clean: true, workspace: { repos: [...] } } result; degrades the whole task to needsHuman if GitHub is unconfigured or any sub-repo is rate-limited, rather than opening a partial subset of PRs.
- Leave existing { mode: "git" | "ai" | "pr" } shapes, the autoMerge:true workspace path, and FN-7554's single-repo PR path unchanged.
- Add engine real-git coverage (task-revert-workspace-pr.real-git.test.ts) and extend dashboard route tests (task-revert-route.test.ts) for the new workspace PR path.
- Add changeset (.changeset/fn-7577-workspace-pr-revert.md, minor) and update docs/task-management.md.
Files changed:
.changeset/fn-7577-workspace-pr-revert.md | 7 +
docs/task-management.md | 3 +-
.../src/__tests__/task-revert-route.test.ts | 313 ++++++++++++++++-
.../src/routes/register-task-workflow-routes.ts | 181 +++++++++-
.../task-revert-workspace-pr.real-git.test.ts | 371 +++++++++++++++++++++
packages/engine/src/index.ts | 4 +
packages/engine/src/task-revert.ts | 295 ++++++++++++++++
7 files changed, 1166 insertions(+), 8 deletions(-)
Fusion-Task-Id: FN-7577
Fusion-Task-Lineage: bedbfab7-5804-485f-9b40-64531edfc64a
Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
decidePlannerRecovery fell through to inject_guidance for any non-failed
executor/workflow-gate signal, including the healthy `progressing` signal.
Under autonomous oversight this dispatched steering into the live agent of
every healthy task — flipping the card badge to "recovering", burning a
bounded-attempt slot, and consuming AI usage for no reason.
- Only problem signals (`stuck`/`blocked`, plus the existing `failed` path)
now trigger autonomous steering; healthy (`progressing`/`complete`) and
human-wait (`awaiting-human`) signals return `none`.
- PlannerRecoveryController.tick clears stale attempt/last-action records for
a (taskId, stage) once its signal is healthy, so a recovered task drops
from "recovering" back to "watching" and a later problem gets a fresh budget.
- PlannerOverseerMonitor dedupes the activity-feed heartbeat: an unchanged
(stage, signal, reason) observation logs once per change, not every tick.
Invariant tests added across all signals for both fall-through stages.
Fusion-Task-Id: FN-7577
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Adds a PR-based revert path for done/archived tasks in autoMerge:false projects instead of refusing outright.
- New engine export `prepareRevertPrBranch` (packages/engine/src/task-revert.ts) prepares a dedicated `fusion/revert-<id>` branch off the base branch's HEAD and applies the revert commit(s) there, never mutating the base branch itself.
- `POST /api/tasks/:id/revert` route gains an additive `{ mode: "pr", clean: true, prUrl, prNumber, revertBranch, existingPr? }` result for clean single-repo reverts under autoMerge:false, reusing GitHubClient.createPr, findPrForBranch idempotency, and the manual:true PR handoff.
- Existing `{ mode: "git" | "ai", ... }` result shapes and the autoMerge:true path are unchanged.
- Workspace (multi-repo) tasks are explicitly refused for PR-based revert (out of scope; single PR cannot represent a multi-repo revert).
- Adds real-git integration tests for the new branch-prep/apply/commit flow and expands the dashboard route test coverage.
- Adds a changeset for @runfusion/fusion (minor) and a small task-management doc update.
Files changed:
.changeset/fn-7554-pr-based-revert.md | 7 +
docs/task-management.md | 2 +-
.../src/__tests__/task-revert-route.test.ts | 196 +++++++++++++++++++-
.../src/routes/register-task-workflow-routes.ts | 182 ++++++++++++++++++
.../src/__tests__/task-revert-pr.real-git.test.ts | 206 +++++++++++++++++++++
packages/engine/src/index.ts | 3 +
packages/engine/src/task-revert.ts | 154 +++++++++++++++
7 files changed, 747 insertions(+), 3 deletions(-)
Fusion-Task-Id: FN-7554
Fusion-Task-Lineage: 9b1bfd82-2428-4cf7-9b36-77afe3517a14
Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
isPhantomExecutorBinding's liveness gate (heartbeat/checkout/runAudit) is
structurally blind to ephemeral executor agents, leaving only the
age>graceMs*3 (~30 min) threshold, so any ephemeral-executor task running
longer than ~30 min was reclaimed to `todo` mid-flight and its worktree
destroyed. Add the in-process live-session veto (activeSessionRegistry path /
executingTaskLock / isTaskActive), mirroring the isWorkspaceTaskLive/sessionDead
predicate, and honor clearPhantomExecutorBinding's live-session refusal in
reclaimSelfOwnedBranchConflicts. Legitimate FN-6736 leaked-binding recovery is
preserved (empty registry / no lock / inactive task still reads as phantom).
Fusion-Task-Id: FN-7566
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Subscription OAuth is aliased across the legacy `anthropic` id (where login
persists the credential) and `anthropic-subscription` (where the settings card
and status read are keyed). After an in-session logout, re-login wrote only
`anthropic` and never cleared the in-memory `anthropic-subscription` logged-out
flag, so the card reported "Login did not complete" despite a valid stored
credential until the process restarted.
auth-storage's proxy now clears the logged-out suppression on both aliases when
either is re-authenticated (new `login` trap + hardened `set` trap via
clearReauthenticatedLogoutState); raw api_key writes stay scoped to their own
card. Also surface previously-swallowed background OAuth login failures on
GET /auth/status (`loginError`) plus server logs and a settings toast, so real
paste-callback failures are diagnosable instead of a generic error.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The release-authorization classifier substring-matched release signals
(notably `scripts/release.mjs`) even inside disclaimer clauses that
explicitly say the task performs NO release/publish. AI-authored specs
routinely append such disclaimers, so revert/undo/UI tasks (FN-7525,
FN-7554, FN-7556) were parked in awaiting-release-authorization with no
in-band exit — their non-user sources (agent_heartbeat/api) make the
authorization marker inert.
classifyReleaseTask now strips negated release-disclaimer clauses before
signal matching. Genuine "run pnpm release"/"publish @runfusion/fusion"
intent lives in a non-negated clause and still trips the gate. Tests
cover all three real repro shapes plus every documented signal in both
its negated (not release-class) and actionable (still release-class) form.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Unifies OAuth expiry detection so expired Claude subscription logins correctly show as disconnected with a re-login prompt, and adds a proactive engine-side scheduler that refreshes tokens before they expire.
- Share expiry-detection logic between OAuthExpiryMonitor and the /api/auth/status route so both agree on when a token is expired.
- Add engine-side oauth-refresh-scheduler that proactively refreshes OAuth tokens ahead of expiry, wired into project-engine (guarded by skipNotifier).
- Extend auth-storage with the helpers needed for expiry checks/refresh.
- Add tests covering routes-auth status detection, auth-storage expiry helpers, and the new refresh scheduler.
- Document the new behavior in dashboard-guide.md and settings-reference.md.
- Add changeset for the user-facing fix.
Files changed:
.../fn-7574-oauth-expiry-detection-refresh.md | 7 +
docs/dashboard-guide.md | 4 +
docs/settings-reference.md | 4 +
.../dashboard/src/__tests__/routes-auth.test.ts | 76 +++++++++++
.../dashboard/src/routes/register-auth-routes.ts | 25 +++-
packages/engine/src/__tests__/auth-storage.test.ts | 60 +++++++++
packages/engine/src/auth-storage.ts | 14 +-
.../__tests__/oauth-refresh-scheduler.test.ts | 141 ++++++++++++++++++++
packages/engine/src/notification/index.ts | 3 +
.../src/notification/oauth-refresh-scheduler.ts | 143 +++++++++++++++++++++
packages/engine/src/project-engine.ts | 14 +-
11 files changed, 488 insertions(+), 3 deletions(-)
Fusion-Task-Id: FN-7574
Fusion-Task-Lineage: 59996eac-c070-4992-9727-d066c6934b69
Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
Manual plan approval now skips re-asking for approval when a re-specification produces an identical plan to one already approved.
- Add nullable Task.approvedPlanFingerprint field with DB migration 139 to track the approved PROMPT.md fingerprint
- Skip re-parking at awaiting-approval when replan/plan-review-retry/self-healing rebound yields the same plan fingerprint as before
- Require fresh approval when the plan content changes or when a plan is rejected
- Leave Release Authorization, Workflow Plan Review, and auto-approve-all behavior unchanged
- Add/extend tests across core (db, plan-approval, store-persistence), engine (triage), and dashboard (routes-github) to cover fingerprint comparison and idempotent re-approval
- Update docs (settings-reference.md, workflow-steps.md) to describe the idempotent approval behavior
- Add changeset for @runfusion/fusion (patch)
Files changed:
.changeset/fn-7569-plan-approval-idempotent.md | 7 +
docs/settings-reference.md | 2 +-
docs/workflow-steps.md | 2 +
packages/core/src/__tests__/db.test.ts | 54 +++++++
packages/core/src/__tests__/plan-approval.test.ts | 31 +++-
.../core/src/__tests__/store-persistence.test.ts | 39 +++++
packages/core/src/db.ts | 22 ++-
packages/core/src/index.ts | 2 +-
packages/core/src/plan-approval.ts | 23 +++
packages/core/src/store.ts | 20 ++-
packages/core/src/types.ts | 13 ++
.../dashboard/src/__tests__/routes-github.test.ts | 69 +++++++-
.../src/routes/register-task-workflow-routes.ts | 37 ++++-
packages/engine/src/__tests__/triage.test.ts | 178 ++++++++++++++++++++-
packages/engine/src/triage.ts | 58 +++++--
15 files changed, 527 insertions(+), 30 deletions(-)
Fusion-Task-Id: FN-7569
Fusion-Task-Lineage: 7d3855ae-6f45-4571-90db-cf1ae3b541dd
Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
Adds all-or-nothing git-revert support for multi-repo workspace tasks and wires it into the existing revert route/AI-undo/per-sha machinery.
- Add `resolveWorkspaceTaskRevertCommits` and `revertWorkspaceTask` to `packages/engine/src/task-revert.ts`, dry-run classifying every sub-repo first and only committing per-repo revert commits when every sub-repo is clean/already-reverted; any conflicting sub-repo rolls back every already-committed sub-repo.
- Extract shared `applyAndCommitRevert" apply/commit machinery (built on the existing `applyRevertNoCommit` primitive) so the workspace path reuses the same commit-message/trailer contract as the single-repo path.
- Add a defensive `isWorkspaceTask` guard to `performTaskRevert` so workspace tasks can never be silently reverted through the single-repo path.
- Wire `POST /api/tasks/:id/revert` (register-task-workflow-routes.ts) to dispatch workspace tasks to `revertWorkspaceTask`, preserving the existing `mode` (git/ai/auto) and AI-undo-fallback contract for workspace conflicts.
- Export the new workspace revert types/functions from `packages/engine/src/index.ts`.
- Add route-dispatch and real-git workspace revert test coverage; update docs and add a changeset.
Files changed:
.changeset/fn-7547-workspace-task-revert.md | 7 +
docs/task-management.md | 8 +-
packages/dashboard/src/__tests__/task-revert-route.test.ts | 102 +++++
packages/dashboard/src/routes/register-task-workflow-routes.ts | 76 +++-
packages/engine/src/__tests__/task-revert.workspace.real-git.test.ts | 272 +++++++++++++
packages/engine/src/index.ts | 6 +
packages/engine/src/task-revert.ts | 448 ++++++++++++++++++++-
7 files changed, 897 insertions(+), 22 deletions(-)
Fusion-Task-Id: FN-7547
Fusion-Task-Lineage: b1b5eeda-06fd-43c1-8163-74b62c77b000
Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
Disambiguate release-authorization approval holds from manual plan-approval holds so auto-approve no longer appears broken.
- Add `Task.awaitingApprovalReason` (`"release-authorization" | null`) to distinguish the release-authorization gate from the independent manual plan-approval gate, both of which set `status: "awaiting-approval"`.
- Stamp `awaitingApprovalReason: "release-authorization"` when the release gate blocks a task, and explicitly clear it (`null`) when the manual plan-approval gate parks the task, so a stale reason never survives a replan.
- Add DB migration/persistence support for the new column in `db.ts`/`store.ts`/`types.ts`.
- TaskCard/TaskDetailModal now render a distinct status for release-authorization holds and suppress the generic manual Approve/Reject affordance for them.
- Add i18n string and docs updates (`settings-reference.md`, `workflow-steps.md`) plus a changeset.
- Extend regression tests in db, triage, TaskCard, and TaskDetailModal to cover the new reason field and disambiguated UI.
Files changed:
$(git diff --cached --stat)
Fusion-Task-Id: FN-7559
Fusion-Task-Lineage: 0b37cbf0-40a4-4165-8088-482ed365ba19
Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
Adds an opt-in per-sha commit granularity mode to the task-revert git path, alongside its default squash behavior, and merges it cleanly with the existing FN-7524 AI-undo mode support.
- Add `TaskRevertGranularity` ("squash" | "per-sha") and thread an optional `granularity` option through `performTaskRevert`/`PerformTaskRevertOptions`.
- Factor a shared `applyRevertNoCommit` primitive (stage + no-op/conflict detection) used by both the squash and new per-sha apply paths.
- `"per-sha"` creates one attributed `revert(FN-xxxx): ...` commit per original sha (each with its own `Fusion-Task-Id` trailer and audit line), skipping no-op shas without empty commits; a mid-batch conflict rolls the whole batch back to the pre-call HEAD.
- Extend `TaskRevertResult`'s clean shape with `revertCommitShas: string[]` (all created commits) alongside the existing `revertCommitSha`.
- `POST /api/tasks/:id/revert` accepts an optional `granularity` request-body field (default `"squash"`, validated, 400 on unknown values) and forwards it to the engine service; documented alongside the existing `mode` (git/ai/auto) contract.
- Add real-git and route-level test coverage for per-sha creation, no-op skipping, default-squash behavior, and mid-batch conflict rollback.
- Update docs/task-management.md's revert section and add a changeset.
Files changed:
.changeset/fn-7548-per-sha-revert-granularity.md | 7 +
docs/task-management.md | 3 +-
packages/dashboard/src/__tests__/task-revert-route.test.ts | 46 +++++-
packages/dashboard/src/routes/register-task-workflow-routes.ts | 51 ++++--
packages/engine/src/__tests__/task-revert.real-git.test.ts | 124 +++++++++++++++
packages/engine/src/index.ts | 2 +
packages/engine/src/task-revert.ts | 176 +++++++++++++++++----
7 files changed, 359 insertions(+), 50 deletions(-)
Fusion-Task-Id: FN-7548
Fusion-Task-Lineage: b9548f5e-fcc2-45d4-98e0-dd7340928208
Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
Adds an AI-undo fallback to the revert route: when a git-based revert conflicts or is unsupported, an ordinary board task is created to perform the undo via AI instead of a forced/failed git write.
- POST /tasks/:id/revert now accepts an optional `{ mode?: "git" | "ai" | "auto" }` body (default "auto"); unknown values reject with 400.
- "git" preserves the FN-7523 git-only contract unchanged; "ai" always creates the AI-undo task; "auto" tries git first and falls back to AI only on a conflicting or unsupported (e.g. workspace) result — needsHuman (autoMerge:false) never triggers the fallback.
- New engine helpers in task-revert.ts: `createAiUndoTask`, `buildAiUndoTaskDescription`, `REVERT_OF_METADATA_KEY`, plus `AiUndoTaskResult`/`CreateAiUndoTaskDeps` types, exported from packages/engine/src/index.ts.
- The AI-undo task is created via the normal triage-column `store.createTask` path with no dependency on the source task, referencing the source task's mission, id, and landed files, and instructing an undo commit using the `revert(FN-xxxx): ...` convention.
- New core `TaskStore.findOpenRevertTaskForSource` backs an idempotency guard: a repeated call while an AI-undo task is still open returns the same `createdTaskId` with `alreadyOpen: true` instead of creating a duplicate.
- Updated docs/task-management.md's revert section to document the git path + AI-undo fallback contract.
- Added a minor changeset for the @runfusion/fusion release notes.
- Added/extended tests: packages/engine/src/__tests__/task-revert-ai-undo.test.ts (new) and packages/dashboard/src/__tests__/task-revert-route.test.ts (extended) covering mode validation, auto-fallback-on-conflict, forced "ai" mode, and the duplicate-open-task guard.
Files changed:
.changeset/fn-7524-ai-undo-revert.md | 7 +
docs/task-management.md | 13 +-
packages/core/src/store.ts | 31 +++++
packages/dashboard/src/__tests__/task-revert-route.test.ts | 143 ++++++++++++++++++++-
packages/dashboard/src/routes/register-task-workflow-routes.ts | 75 +++++++++--
packages/engine/src/__tests__/task-revert-ai-undo.test.ts | 114 ++++++++++++++++
packages/engine/src/index.ts | 5 +
packages/engine/src/task-revert.ts | 117 ++++++++++++++++-
8 files changed, 487 insertions(+), 18 deletions(-)
Fusion-Task-Id: FN-7524
Fusion-Task-Lineage: 64dfedcf-c286-4c46-8cf8-51ec5e668bf7
Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
## Summary
Two related mission-loop fixes, both observed wedging a live autonomous
board.
### 1. Triage ignores `mission.branchStrategy` when `branchAssignment`
is omitted (dashboard)
`resolveBranchAssignmentContext` fabricated `{ mode: "shared" }` for
absent input, so the mission triage routes (`triage`, `triage-all`)
always passed an explicit `assignmentMode` into
`missionStore.triageFeature`/`triageSlice`. That defeats the store's
fallback — `branchOptions?.assignmentMode ??
strategyDefaults.assignmentMode` — so a mission configured with
`branchStrategy: auto-per-task` still produced a **shared** branch
group, named after the base branch.
docs/missions.md documents the intended behavior: missions "can also
persist a `branchStrategy` used whenever triage is triggered without
explicit branch options."
**Fix:** absent input resolves to `{ mode: undefined }`; callers pick
their own default. The mission routes need no change (undefined now
flows through to the strategy fallback). The two planning-subtask call
sites keep their historical `shared` default via a destructure default,
since they have no strategy to fall back to. Explicit
`branchAssignment.mode` is unchanged and still overrides the strategy.
**Observed impact:** with `baseBranch: main`, every triaged task joined
a shared group literally named `main` — tasks tried to push to `main` /
open PRs with head=main base=main, and the whole group wedged in
`merge-retries-exhausted`. The only workaround was remembering to send
`{"branchAssignment": {"mode": "per-task-derived"}}` on every triage
call, which silently ignores the mission's configured strategy the rest
of the time.
### 2. Task-completion validation runs for parked missions (engine)
`MissionExecutionLoop.processTaskOutcome` validated every completed
feature-linked task with no mission-status check — unlike
`recoverActiveMissions`, which already skips missions with `status !==
"active"`. A parked mission (`status: planning`) kept minting validator
runs, and on validator failure, new "Fix:" features — for tasks that
completed after parking. On our board a stale validator workspace
produced a `Fix: → Fix: Fix: → Fix: Fix: Fix:` spiral of bogus features
for already-merged work; the only mitigation was re-parking the mission
after every release and manually archiving the minted features.
**Fix:** gate `processTaskOutcome` on the resolved mission being active,
mirroring the `recoverActiveMissions` guard. The gate sits before the
`needs_fix → implementing` transition so an inactive mission's features
get zero state mutation; the skip logs a `warning` mission event
(`validation_skipped_mission_inactive`) so it's visible in the mission
log. Features that don't resolve to a mission keep the current behavior.
(Out of scope but worth noting: the validator that triggered the spiral
was judging merged work against a stale workspace checkout — that
freshness issue is a separate problem this PR doesn't attempt.)
## Tests
- `branch-selection.test.ts` — updated: absent/`{}` input resolves
`mode: undefined`; explicit modes and the bad-mode error unchanged.
- `mission-execution-loop.test.ts` — two new tests: parked mission skips
validation and logs the warning event; active mission still validates.
- Existing `mission-store.test.ts` coverage ("uses mission
branchStrategy … when branch options are omitted", explicit `shared`
override still creates a group) pins the store side end-to-end — those
pass unchanged, as do the planning/branch-group route suites (182 tests)
and full workspace `pnpm typecheck`.
Changeset included (`patch`, category `fix`).
🤖 Generated with [Claude Code](https://claude.com/claude-code)
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit
* **Bug Fixes**
* Branch selection now keeps an unspecified mode unset and falls back to
the mission’s configured branch strategy where appropriate.
* Task outcome processing now skips validation for missions that are not
active, preventing unnecessary follow-up actions.
* Added coverage for branch selection and mission execution behavior to
verify the updated handling.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
Wires PlannerOverseerMonitor/PlannerRecoveryController decision points (human-control withholds, confirmation requests/resolutions, and related overseer stages) to the FN-7520 emitOverseer* façade using the real TaskStore, so the planner-oversight intervention timeline now populates from real engine activity instead of staying empty.
- Add onConfirmationResolved handler to PlannerRecoveryController, invoked (best-effort, audit-only) from resolveConfirmation for both approved and denied outcomes.
- Wire project-engine.ts to call emitOverseerObservation/emitOverseerEscalation/emitOverseerConfirmation at the real engine decision points, deduped per (task, stage[, signal]).
- Add planner-overseer-intervention-wiring.test.ts covering the new wiring end-to-end.
- Update docs/architecture.md to reflect the wiring.
- Add changeset fn-7551-overseer-timeline-wiring.md (patch).
Files changed:
.changeset/fn-7551-overseer-timeline-wiring.md | 7 +
docs/architecture.md | 2 +-
.../planner-overseer-intervention-wiring.test.ts | 319 +++++++++++++++++++++
packages/engine/src/planner-recovery-controller.ts | 36 +++
packages/engine/src/project-engine.ts | 248 +++++++++++++++-
5 files changed, 607 insertions(+), 5 deletions(-)
Fusion-Task-Id: FN-7551
Fusion-Task-Lineage: 8bcd103e-8797-4ef5-9b68-bd2daec8d26b
Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
Branch-group completion no longer silently drops archived-but-unlanded members, which previously let genuinely-incomplete groups be flagged complete and promoted.
- listTasksByBranchGroup now scans with includeArchived:true so archived members stay counted in the group's total instead of dropping out silently
- ArchivedTaskEntry gains a persisted mergeDetails snapshot so an archived member that had already landed is still distinguished from one that never landed
- store.ts archival paths (task->archive projection) now carry mergeDetails through so isBranchGroupMemberLanded keeps working post-archival
- Added regression coverage in branch-group-store.test.ts and group-merge-coordinator.test.ts for archived-landed and archived-unlanded gating
- Added changeset documenting the fix as a patch-level bug fix
Files changed:
.changeset/fn-7534-branch-group-archived-member.md | 7 +
docs/dashboard-guide.md | 2 +
packages/core/src/__tests__/branch-group-store.test.ts | 77 +++++++++++
packages/core/src/store.ts | 30 ++++-
packages/core/src/types.ts | 11 ++
packages/engine/src/__tests__/group-merge-coordinator.test.ts | 148 ++++++++++++++++++++-
6 files changed, 273 insertions(+), 2 deletions(-)
Fusion-Task-Id: FN-7534
Fusion-Task-Lineage: 510af857-ce08-49a0-a2a1-41b3ad473804
Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
processTaskOutcome validated every completed feature-linked task with no
mission-status check, unlike recoverActiveMissions (which already skips
missions with status !== "active"). A parked mission (status=planning) kept
minting validator runs — and "Fix:" features on failure — for tasks completed
after parking, spiraling Fix^n features from a stale validator workspace.
Gate processTaskOutcome on the resolved mission being active, before the
needs_fix -> implementing transition so parked missions get zero feature
state mutation. The skip logs a warning mission event
(validation_skipped_mission_inactive). Features that don't resolve to a
mission keep the current behavior.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
## What
Adds a new built-in workflow **Coding (Ideas)** — a capture-first
variant of the default coding pipeline that puts a manual **Ideas (no
AI)** intake in front of a merged **Todo** (planner + capacity) column.
The board becomes five stages, each staffed by a distinct role:
```
Ideas (no AI) → Todo (planner) → In-progress (coder) → In-review (reviewer) → Done
```
## Why
The current board parks un-worked cards in a passive **Todo** column
where no agent is active. Operators asked for a way to (1) capture ideas
without the engine auto-planning them, and (2) collapse the triage/todo
split so every visible column has an agent working it — planning now
happens *in* Todo. A "Ready" badge distinguishes planned cards waiting
for a capacity slot from freshly promoted unplanned ones.
## How it works
1. **Create** a task against Coding (Ideas) → it lands in **Ideas**
(`autoTriage:false` intake). The triage service ignores it — no AI runs.
2. **Start** (button on the card, or drag) moves it to **Todo**. The
triage poll discovers the unplanned card (bootstrap-stub PROMPT.md) and
plans it in place.
3. While planning the card shows **Planning**; once the spec is written
it shows **Ready** and waits for an in-progress slot under the normal
capacity hold.
4. From Todo onward the graph is identical to the default Coding
workflow (stepwise execution → optional code review → merge).
## Engine changes
| Surface | Change |
|---|---|
| `createTask` (`store.ts`) | Lands cards in the workflow's intake
column (`resolvedEntryColumn`) instead of hardcoding `"triage"`. Default
workflow is byte-identical (intake resolves to `"triage"`).
Bootstrap-prompt check generalized to all pre-planning columns. |
| Triage poll (`triage.ts`) | Also discovers unplanned `todo` tasks
(bootstrap-stub prompt); `finalizeApprovedTask` skips the redundant
triage→todo move for in-place planning; planning-concurrency counter
covers both columns. |
| Scheduler (`scheduler.ts`) | Skips `todo` tasks with
`status:"planning"` or a bootstrap-stub prompt so unplanned cards are
never dispatched. |
| TaskCard (`TaskCard.tsx`) | **Start** button on ideas cards; **Ready**
badge on planned todo tasks. |
| Board (`board-workflows.ts`) | `ideas` column label. |
All engine changes are **gated** — they only affect workflows whose
intake is not `"triage"`, so the default Coding workflow and every
existing built-in are byte-identical in behavior.
## Tests
- `builtin-coding-ideas-workflow-ir.test.ts` *(new)* — column set,
intake trait (`autoTriage:false`), merged todo traits, node re-homing
(start→ideas, planning→todo), optional-group defaults, round-trip.
- `store-create-intake-column.test.ts` *(new)* — createTask lands in
`ideas` for explicit + default selection, `triage` for the default
workflow, writes a bootstrap prompt.
- Updated `builtin-workflows.test.ts` catalog-order assertion for the
new entry.
## Verification
- Typecheck: core ✓ engine ✓ dashboard ✓
- Lint ✓ · Changeset format ✓ (`minor`)
- Merge gate (`test:gate`): 321 engine-core + 63 CI-shape ✓
- Regression suites: triage (39), concurrency (165),
movement/migration/hooks (259), builtin workflows (65), store-create
(54) — all green
- `verify:fast`: workspace build + CLI build + boot smoke (`fn --help` +
real `/api/health`) ✓
## Changeset
`.changeset/fn-coding-ideas-workflow.md` — `@runfusion/fusion: minor`
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit
* **New Features**
* Added a new “Coding (Ideas)” builtin workflow with an Ideas intake
stage and merged planning flow.
* Updated task cards to support a **Start** action and show a **Ready**
badge for qualifying planning-stage tasks.
* **Bug Fixes**
* Tasks created for the Ideas workflow now persist into the correct
entry column and get the right prompt bootstrapping.
* Scheduler and triage avoid promoting/releasing unplanned todo tasks
that still contain the bootstrap prompt stub, and stale planning is
cleaned up across the merged intake flow.
* **Tests**
* Added coverage for the new builtin workflow IR and create-task intake
wiring.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
- Gate bootstrap prompt to entry column/triage only, not every non-execution
column, so direct createTask({column:'todo'}) keeps generateSpecifiedPrompt.
- Guard the workflow-column hold-release dispatch path (reserveSlot) against
planning-status and bootstrap-stub todo tasks, matching the legacy filter.
- Extend clearStaleSpecifyingStatuses startup sweep to the todo column so a
restarted in-place planning task does not hold a maxTriageConcurrent slot.
- Gate the Start button on the intake column flag instead of the literal
'ideas' id, so any manual-intake workflow gets the affordance.
- Add regression test: direct todo create must not get a bootstrap stub.
Require generated task specs to summarize the requested before-to-after transformation near the top.
- Add a Before → After Transformation section to standard and fast planning prompt templates.\n- Document the new task definition section and cover it with prompt regression tests.\n- Add a patch changeset for the published Fusion package.\n\nFiles changed:\n .changeset/fn-7499-before-after-transformation.md | 7 +++++++\n docs/task-management.md | 1 +\n packages/core/src/__tests__/agent-prompts.test.ts | 17 +++++++++++++++\n packages/core/src/agent-prompts.ts | 25 +++++++++++++++++++----\n packages/engine/src/__tests__/triage.test.ts | 20 ++++++++++++++++++\n 5 files changed, 66 insertions(+), 4 deletions(-)
Fusion-Task-Id: FN-7499
Fusion-Task-Lineage: d049b5d6-a4bc-40dd-831d-04a11f9dc2cf
Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
Add builtin:coding-ideas, a capture-first variant of the default coding
pipeline. New cards land in a manual Ideas intake (autoTriage:false) and are
not auto-planned until an operator promotes them into the merged Todo
planner+capacity column, where the triage service plans them in place.
Engine foundation:
- createTask lands cards in the workflow intake column (resolvedEntryColumn)
instead of hardcoding triage; default workflow is byte-identical.
- Triage poll discovers unplanned todo tasks (bootstrap-stub prompt) and
plans them in place; finalizeApprovedTask skips the redundant move.
- Scheduler skips todo tasks that are planning or still carry a bootstrap
prompt, so unplanned cards are never dispatched.
Dashboard:
- Start button on ideas cards (ideas -> todo move triggers planning).
- Ready badge on planned todo tasks waiting for an in-progress slot.
- ideas column label in board-workflows.
Tests: workflow IR round-trip/column/node-placement, createTask intake wiring,
and updated builtin catalog order assertion.
- probeWorktrunk: also refuse a bare wt/wt.exe override on Windows (resolves to
Windows Terminal via PATH), and tighten the package-dir match from a broad
'windowsterminal' substring to 'microsoft.windowsterminal' so a genuine
worktrunk under an unrelated *windowsterminal* folder is still probed.
- Fix worktrunk enable deadlock/save-race: probe status when the user views the
Worktrees section (not gated on 'enabled', which deadlocked since the toggle is
disabled until status==installed), and re-verify on Save so a fast enable+save
can't silently persist enabled:false. Hook 'refresh' now returns the fetched
status and is exposed.
- Tests: bare-wt refusal, forward-slash Windows Terminal path, unrelated
windowsterminal-folder is probed, and a rerender enabled false->true probe-once
transition; update SettingsModal mocks for the new refresh().
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The 1882/1883 terminal-auto-create guard fixed the wrong subsystem; the
embedded terminal was already guarded. The actual trigger: worktrunk's CLI is
named 'wt', colliding with Windows Terminal (wt.exe) on PATH, so probing it with
'wt --version' launched Windows Terminal and popped its native version dialog —
fired automatically by the Settings worktrunk-status fetch on mount.
- useWorktrunkInstallStatus: only auto-fetch /api/worktrunk/status when the
integration is enabled (user opt-in), never on a plain Settings/dashboard mount.
- probeWorktrunk: refuse to exec a resolved 'wt' that is the Windows Terminal
alias (WindowsApps / WindowsTerminal package dir), covering every resolution
surface (cached/override/PATH/install/settings-route). Basename computed
host-independently so the guard holds when the build host is POSIX.
- Tests for both guards; report updated with corrected root cause + Symptom
Verification + Surface Enumeration.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>