Commit Graph

9434 Commits

Author SHA1 Message Date
gsxdsm
f7d1346724 Merge branch 'main' into fix/validator-premerge-guard 2026-07-05 21:04:34 -07:00
gsxdsm
78d570747f fix(engine): self-heal failed in-review cards whose PR merged on the remote (#1922)
## Problem

A transient error at merge time can flag an in-review card `failed` even
when its PR actually squash-merged on the remote (human merge,
merge-train, etc.). `recoverAlreadyMergedReviewTasks` runs the
already-merged **evidence detector** only against the **local** base
ref. If this process never fetched the merge, the owned commit is absent
locally → the detector returns `null` → `landed` is null → the card
never finalizes and **holds its file-scope lease forever**, wedging
every other task that touches the same files.

## Fix — fetch-then-prove

When a `failed` in-review candidate has a recorded PR
(`getPrimaryPrInfo`) and the local base yields no owned commit:

1. best-effort `git fetch origin <base>` (new `refreshRemoteBaseRef`
helper), then
2. re-run the **same** evidence detector against `origin/<base>`.

The detector's owned-commit proof and every foreign-ownership guard
inside it remain the **sole** finalize gate, so this only un-wedges a
genuinely-merged task — it never phantom-finalizes on unproven state.

**Safety:**
- Gated on a recorded PR — no PR ⇒ nothing could have merged remotely ⇒
no fetch.
- Fail-closed — a fetch error (offline / auth / no remote) is swallowed;
if `origin/<base>` can't be resolved the card is left untouched.
- No new dependency, no github client seam — direct git only.

## Tests

Two real-git tests in `self-healing-already-merged.real-git.test.ts`,
both verified to **fail without the prod change**:

- **fetch-then-prove positive:** a PR squash-merges on a bare remote
while the local base stays stale → recovery fetches, proves the owned
SHA against `origin/main`, and finalizes the card to `done`
(`mergeConfirmed: true`, worktree removed).
- **phantom-finalize guard:** remote base advances with a commit owned
by a *different* task → the fetch still runs, but the detector proves
nothing → the card is left `failed`/`in-review`, never healed.

Full self-heal suite: 594 passed. Engine typecheck clean.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

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

## Summary by CodeRabbit

* **Bug Fixes**
* Improved recovery for review tasks that appear already merged when the
local base branch is stale.
* The app now refreshes the remote base branch before re-checking merge
status, helping finalize tasks correctly and clean up completed
worktrees.
* Added coverage for cases where the remote base has moved forward with
either the merged commit or unrelated changes.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2026-07-05 20:59:01 -07:00
gsxdsm
ba9c9b5ca7 Merge branch 'main' into fix/self-heal-merged-pr-stale-base 2026-07-05 20:47:22 -07:00
gsxdsm
80a05e41fe Merge branch 'main' into fix/hermes-runtime-chat-state 2026-07-05 20:45:47 -07:00
gsxdsm
eb86555797 chore(release): v0.56.1
Version bump via changesets.
2026-07-05 19:57:09 -07:00
gsxdsm
f4f165640a FN-7607: fix manual PR flow gating to key off global auto-merge setting
Fixes TaskDetailModal so manual PR affordances stay visible based on the live global auto-merge setting rather than the per-task effective override, and repairs a pre-existing test regression from the FN-7510 oversight default change.

- isManualPrFlow now checks mergeStrategy === "pull-request" && !autoMergeEnabled (live global setting) instead of the per-task effective auto-merge override, fixing a regression from FN-7255 that stranded users without manual PR controls when a task's auto-merge override was true but global auto-merge was off.
- Pinned plannerOversightLevel: "off" on the Chat-first default-routing test fixture so the FN-7510 autonomous-oversight default doesn't add an extra Activity-view option and break the test's actual intent (asserting Chat-first tab routing).
- Added changeset documenting the fix.

Files changed:
 .changeset/fn-7607-manual-pr-flow.md                       |  7 +++++++
 packages/dashboard/app/components/TaskDetailModal.tsx      | 14 +++++++++++++-
 .../TaskDetailModal.attachments-and-tabs.test.tsx          | 12 +++++++++++-
 3 files changed, 31 insertions(+), 2 deletions(-)

Fusion-Task-Id: FN-7607

Fusion-Task-Lineage: f0b077d4-792f-4e43-8e40-43d325920be5

Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
2026-07-05 19:51:35 -07:00
gsxdsm
e347062e1f FN-7603: force xterm DOM-based char measurement to fix mobile terminal spacing
Fixes recurrence #5 of mobile terminal inter-character spacing by unifying xterm's cell-width measurement pipeline with WidthCache's DOM-based glyph measurement, validated against real xterm instead of the jsdom mock.

- Add withDomBasedTerminalCharacterMeasurement() in terminalPreferences.ts: transiently hides window.OffscreenCanvas during terminal.open() so CharSizeService's constructor throws and self-selects its own DOM-based fallback strategy, unifying dimensions.css.cell.width with WidthCache.get('W') measurement
- Wire withDomBasedTerminalCharacterMeasurement() around terminal.open() calls in SessionTerminal.tsx and TerminalModal.tsx
- Add FNXC:Terminal comments documenting the Canvas-vs-DOM measurement divergence root cause, grounded in the installed @xterm/xterm@5.5.0 source
- Add docs/solutions/ui-bugs/xterm-options-noop-remeasure-after-font-settle.md recurrence #5 section
- Expand TerminalModal.test.tsx coverage for the new measurement-forcing behavior
- Add changeset fn-7603-mobile-terminal-spacing.md (patch, fix)

Files changed:
 .changeset/fn-7603-mobile-terminal-spacing.md      |   7 +
 docs/solutions/ui-bugs/xterm-options-noop-remeasure-after-font-settle.md | 101 ++++++
 packages/dashboard/app/components/SessionTerminal.tsx   |  16 +-
 packages/dashboard/app/components/TerminalModal.tsx     |  16 +-
 packages/dashboard/app/components/__tests__/TerminalModal.test.tsx    | 363 ++++++++++++++++++++-
 packages/dashboard/app/utils/terminalPreferences.ts     |  63 ++++
 6 files changed, 554 insertions(+), 12 deletions(-)

Fusion-Task-Id: FN-7603
Fusion-Task-Lineage: 6c7d980f-953e-4fa9-908e-b24125904cbe
Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
2026-07-05 19:42:16 -07:00
gsxdsm
f7dfcb3b09 FN-7604: collapse overseer/oversight controls into a single dropdown across surfaces
Unifies the previously mobile-only Oversight overflow menu into a single, always-present dropdown that replaces the scattered desktop oversight buttons and the separate mobile affordance.

- Replace discrete desktop oversight action buttons in TaskDetailModal's footer with one universal "Oversight actions" dropdown trigger, reusing the menu across desktop and mobile breakpoints.
- Simplify TaskDetailModal.tsx footer rendering logic, removing now-redundant responsive branching for oversight controls.
- Update TaskDetailModal.css to drop the old mobile-only oversight-overflow styles and support the unified dropdown across breakpoints.
- Update definition-actions, oversight-controls, oversight-mobile, rendering, and responsive-and-dependencies tests to assert the single dropdown behavior and disambiguate the exact "Actions" button query from the new "Oversight actions" aria-label.
- Refresh docs/dashboard-guide.md to describe the unified oversight dropdown UX.

Files changed:
 docs/dashboard-guide.md                            |  14 +-
 packages/dashboard/app/components/TaskDetailModal.css   |  89 +++++------
 packages/dashboard/app/components/TaskDetailModal.tsx   | 177 +++------------------
 packages/dashboard/app/components/__tests__/TaskDetailModal.definition-actions.test.tsx    |  70 ++++----
 packages/dashboard/app/components/__tests__/TaskDetailModal.oversight-controls.test.tsx    | 130 ++++++++++-----
 packages/dashboard/app/components/__tests__/TaskDetailModal.oversight-mobile.test.tsx      |  42 +++--
 packages/dashboard/app/components/__tests__/TaskDetailModal.rendering.test.tsx   |  27 ++--
 packages/dashboard/app/components/__tests__/TaskDetailModal.responsive-and-dependencies.test.tsx |  57 ++++---
 8 files changed, 278 insertions(+), 328 deletions(-)

Fusion-Task-Id: FN-7604
Fusion-Task-Lineage: afbb7573-d654-48db-a9ac-aecbd8e22e46
Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
2026-07-05 19:39:18 -07:00
gsxdsm
8fb7d51ae9 FN-7601: distinguish task-detail priority chip colors by level
Give the TaskDetailModal priority chip distinct tinted borders/backgrounds per level so low/high/urgent/normal are visually distinguishable at a glance.

- Add per-level border-color and stronger background overrides for .detail-priority-chip.card-priority-badge--{low,high,urgent}, using the matching semantic color token (info/warning/error) with higher specificity than the shared base rule.
- Leave the FN-7585 shared base chip rule and FN-7597 neutral 'normal' treatment untouched; scope changes strictly to .detail-priority-chip so read-only TaskCard badge tints are unaffected.
- Add a regression test asserting each level has distinct, non-var(--border) border-colors and backgrounds, mutually distinct across levels, while the read-only TaskCard badge selectors remain unchanged.

Files changed:
 .../dashboard/app/components/TaskDetailModal.css   | 33 ++++++++++
 ...etailModal.responsive-and-dependencies.test.tsx | 71 ++++++++++++++++++++++
 2 files changed, 104 insertions(+)

Fusion-Task-Id: FN-7601

Fusion-Task-Lineage: 1e2fc574-835c-43b0-8689-02884ad5c2d6

Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
2026-07-05 19:09:14 -07:00
gsxdsm
dc447304a9 fix(FN-7591): allow moving tasks out of custom workflow columns (Coding (Ideas))
Moving a card out of a non-legacy workflow column — e.g. Coding (Ideas)
"ideas" → "todo" — was rejected with "Invalid transition: 'ideas' → 'todo'.
Valid targets: none".

Workflow columns graduated to always-on but moveTaskInternal's compat-flag
legacy branch (the default path, since no experimental flag is emitted)
validated every move against the legacy VALID_TRANSITIONS table, which is
keyed only by the built-in column ids. Default-workflow moves survived by
coincidence (its ids ARE the legacy ids); a task in a custom column had no
key so every move was rejected.

The legacy branch now resolves a non-legacy source column's targets from the
task's own workflow adjacency (resolveAllowedColumns), while keeping the
legacy bare-Error contract intact for legacy columns (transition-parity /
characterization suites unchanged). Adds a regression test covering the
ideas -> todo -> in-progress -> in-review chain and non-adjacent rejection.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-05 19:06:32 -07:00
gsxdsm
c2eb89b8d4 fix: report scope-incapable Anthropic OAuth as not-connected in /auth/status
A present, unexpired Anthropic subscription OAuth token that lacks an
inference scope (e.g. a profile-only grant) authenticates identity but
403s on every model call. /auth/status previously validated only token
presence + expiry, so it reported such a token as connected while all
inference failed. It now treats an inference-incapable Anthropic OAuth
token as not-connected (authenticated:false, expired:true so the
re-login banner fires) with a scope-specific loginError. Gated to
Anthropic providers only; tokens with no recorded scopes are treated as
usable to avoid false negatives on fresh logins.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-05 19:02:15 -07:00
gsxdsm
b9d60b3c39 FN-7602: fix Record/Clear button overlap in Keyboard Shortcuts rows
Fixes overlapping Record and Clear buttons on the Keyboard Shortcuts settings rows by replacing the icon-only button class with a text button class and locking layout with flex-shrink.

- Swap ShortcutCaptureInput Record/Clear buttons off the icon-only `btn-icon` class (which forced line-height:0 and a 36px mobile square, clipping labels) onto a text-button class
- Add `.shortcut-capture` row CSS with `flex-shrink:0` on controls so the input and buttons never overlap and stack cleanly on mobile
- Add regression tests covering the Keyboard Shortcuts section layout
- Add changeset documenting the fix

Files changed:
 .changeset/fn-7602-shortcut-row-layout.md          |  7 ++
 .../dashboard/app/components/SettingsModal.css     | 17 ++++
 .../settings/sections/ShortcutCaptureInput.tsx     | 14 +++-
 .../__tests__/KeyboardShortcutsSection.test.tsx    | 95 ++++++++++++++++++++++
 4 files changed, 131 insertions(+), 2 deletions(-)

Fusion-Task-Id: FN-7602

Fusion-Task-Lineage: 50cf6975-f0fb-42dd-87b0-50578977a0f4

Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
2026-07-05 18:56:24 -07:00
gsxdsm
ed823c794c fix: preserve Claude OAuth scopes on token refresh so inference keeps working
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>
2026-07-05 18:47:40 -07:00
Phil Larson
a734d9f0b9 fix: address Hermes runtime PR review feedback 2026-07-05 18:26:33 -07:00
Phil Larson
8d6c92ac2e fix: preserve Hermes runtime chat session state 2026-07-05 17:50:13 -07:00
gsxdsm
2025f9d56d chore(release): v0.56.0
Version bump via changesets.
2026-07-05 17:13:36 -07:00
gsxdsm
5b193d2d08 FN-7600: fix Nudge control stuck on periodic-observation copy when overseer is active
Attach the transient plannerOverseerState snapshot to the single-task detail route so the Nudge control reflects live overseer observation instead of always showing the periodic-observation message.

- GET /api/tasks/:id now best-effort attaches plannerOverseerState (mirrors the list route), never throwing on enrichment failure.
- TaskDetailModal reads overseerSnapshot from workingTask (merged full-detail object) instead of the raw task prop, so detail refetches via fetchTaskDetail (dependency chips, Documents view, logs, post-open refetch) no longer drop the snapshot.
- Added regression tests for the detail-route enrichment and the modal's Nudge-availability behavior.
- Added a patch changeset documenting the fix.

Files changed:
 .changeset/fn-7600-oversight-nudge-detail-snapshot.md             |   7 ++
 packages/dashboard/app/components/TaskDetailModal.tsx             |  14 ++-
 .../TaskDetailModal.oversight-controls.test.tsx                   | 131 +++++++++++++++++++++
 .../__tests__/tasks-planner-overseer-state.test.ts                |  95 +++++++++++++++
 packages/dashboard/src/routes/register-task-workflow-routes.ts    |  25 +++-
 5 files changed, 269 insertions(+), 3 deletions(-)

Fusion-Task-Id: FN-7600

Fusion-Task-Lineage: 500614d0-091a-461c-8e7b-329a7b791502

Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
2026-07-05 17:03:56 -07:00
gsxdsm
e0f3d3d14c FN-7599: rename triage column label to Planning in default workflows
Renames the default-workflow intake column's display label from "Triage" to "Planning" across the built-in coding, stepwise-coding, and PR workflows, while keeping the column id as `triage` for lifecycle/DB/type stability.

- builtin-coding-workflow-ir.ts: intake column name "Triage" -> "Planning"
- builtin-pr-workflow-ir.ts: intake column name "Triage" -> "Planning"
- builtin-stepwise-coding-workflow-ir.ts: intake column name "Triage" -> "Planning"
- Added regression tests asserting the intake column is labeled "Planning" with id "triage" in builtin-coding and hand-authored default workflows (stepwise-coding, pr-workflow)
- Added changeset (patch) documenting the label change for @runfusion/fusion

Files changed:
 .changeset/fn-7599-planning-column-rename.md                 |  7 +++++++
 .../core/src/__tests__/builtin-coding-workflow-ir.test.ts    |  7 +++++++
 packages/core/src/__tests__/builtin-workflows.test.ts        | 12 ++++++++++++
 packages/core/src/builtin-coding-workflow-ir.ts              |  3 ++-
 packages/core/src/builtin-pr-workflow-ir.ts                  |  2 +-
 packages/core/src/builtin-stepwise-coding-workflow-ir.ts     |  2 +-
 6 files changed, 30 insertions(+), 3 deletions(-)

Fusion-Task-Id: FN-7599

Fusion-Task-Lineage: 5de8abc4-2407-4f9a-b97c-bd5b900d8fd9

Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
2026-07-05 16:58:42 -07:00
gsxdsm
20379e81c5 FN-7597: style task-detail Priority dropdown to match Oversight dropdown
Aligns the task-detail Priority dropdown's size, border, and typography with the Oversight dropdown so both controls read as one consistent style.

- Give the untinted `normal` priority level a neutral, token-based chip background (scoped to `.detail-priority-chip.card-priority-badge--normal`) instead of an empty bordered shell, matching the Oversight `--off` chip treatment.
- Remove the Priority-only forced uppercase text-transform on the select/option so it relies on the ancestor label's uppercase transform like the Oversight select does.
- Add regression coverage asserting shared box-size/border tokens across the Priority chip, Oversight chip, and mobile Oversight overflow trigger, no duplicated text-transform overrides, preserved low/high/urgent semantic tints, and unaffected --saving state.
- Add a patch changeset documenting the fix.

Files changed:
 .changeset/fn-7597-priority-dropdown-matches-oversight.md          |  7 +++
 packages/dashboard/app/components/TaskDetailModal.css              | 27 ++++++++++--
 packages/dashboard/app/components/__tests__/TaskDetailModal.responsive-and-dependencies.test.tsx | 51 ++++++++++++++++++++++
 3 files changed, 82 insertions(+), 3 deletions(-)

Fusion-Task-Id: FN-7597

Fusion-Task-Lineage: d703e59a-35d8-4788-9ad2-1462d6f3c588

Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
2026-07-05 16:56:22 -07:00
gsxdsm
09a1c9d843 FN-7596: regression-test the Coding (Ideas) manual-intake lifecycle end-to-end
Adds cross-layer regression coverage for the manual-intake parking lifecycle (create -> parked -> operator Start promotion -> poll-time todo-discovery), and clarifies the workflow-steps doc to describe the tested lifecycle.

- packages/core: covers store create -> moveTask promotion out of the parked intake column
- packages/engine: covers triage poll ordering/discovery of the still-unplanned bootstrap-stub card
- packages/dashboard: covers TaskCard's Start affordance for parked cards
- docs: documents the full regression-tested lifecycle for manual-intake column parking (FN-7596)

Files changed:
 docs/workflow-steps.md                             |   2 +-
 .../__tests__/store-create-intake-column.test.ts   |  26 ++++
 .../app/components/__tests__/TaskCard.test.tsx     | 153 +++++++++++++++++++++
 packages/engine/src/__tests__/triage.test.ts       | 119 +++++++++++++++-
 4 files changed, 298 insertions(+), 2 deletions(-)

Fusion-Task-Id: FN-7596

Fusion-Task-Lineage: 267c3d9a-6181-4ca5-b871-7009c0204372

Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
2026-07-05 16:33:21 -07:00
gsxdsm
8b4e5224ea fix(FN-7591): stop intake-column cards vanishing from the workflow board
Tasks added to a workflow whose intake column differs from the default
(e.g. Coding (Ideas) -> "ideas") disappeared from the board until a manual
reload. The board resolves a card's lane from the board-workflows
taskWorkflowIds map, which only refetches on mount/focus/workflow-CRUD SSE
-- never on task creation. A freshly created card was absent from that map,
fell back to the default workflow (no "ideas" column), and was dropped from
every lane.

- Board.tsx: force one board-workflows refetch (deferred a tick,
  signature-guarded) whenever a rendered task is missing from taskWorkflowIds,
  so its real workflow + intake column resolve for any create surface.
- Board.tsx: re-home a selected-workflow task whose column the workflow no
  longer declares into the intake lane instead of a phantom bucket.
- useBoardWorkflows.ts: widen refreshBoardWorkflows type to accept forceFresh.
- Add regression tests for tasks arriving via the tasks prop (SSE / non-board
  create surfaces) and the orphan-column safety net.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-05 16:10:36 -07:00
gsxdsm
f30d55fae7 FN-7593: move Before/After Transformation section to top of task definitions
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>
2026-07-05 13:44:50 -07:00
gsxdsm
cf3fe8b485 FN-7591: make dashboard create surfaces resolve intake column from workflow instead of hard-coding triage
Fixes dashboard task creation so new cards land in the selected/default workflow's intake column instead of always forcing legacy triage, letting workflows like Coding (Ideas) park new cards in 'ideas' until an operator promotes them.

- InlineCreateCard, QuickEntryBox, and NewTaskModal no longer hard-code column:"triage"; InlineCreateCard now forwards workflowId at create time instead of applying it post-create.
- Fixed a glue-layer regression in useTaskHandlers.ts (handleBoardQuickCreate/handleModalCreate) that re-forced column:"triage" even after UI surfaces stopped sending it.
- Added/updated tests covering the store's intake-column resolution and the dashboard create surfaces/hooks.
- Documented the new manual-intake-column parking behavior in dashboard-guide.md and workflow-steps.md.
- Added a patch changeset for @runfusion/fusion.

Files changed:
 .changeset/fn-7591-coding-ideas-intake.md          |  7 +++
 docs/dashboard-guide.md                            |  4 ++
 docs/workflow-steps.md                             |  1 +
 packages/core/src/__tests__/store-create-intake-column.test.ts   | 20 ++++++++
 packages/dashboard/app/App.tsx                     |  5 +-
 packages/dashboard/app/components/InlineCreateCard.tsx  | 28 ++++------
 packages/dashboard/app/components/NewTaskModal.tsx |  5 +-
 packages/dashboard/app/components/QuickEntryBox.tsx     |  5 +-
 packages/dashboard/app/components/TodoView.tsx     |  7 ++-
 packages/dashboard/app/components/__tests__/InlineCreateCard.test.tsx | 59 +++++++++++++++++++++-
 packages/dashboard/app/components/__tests__/QuickEntryBox.test.tsx    | 14 +++--
 packages/dashboard/app/components/__tests__/TodoView.test.tsx | 10 ++--
 packages/dashboard/app/components/__tests__/board-quickcreate-workflow-lane-visibility.test.tsx | 45 ++++++++++++++++-
 packages/dashboard/app/hooks/__tests__/useTaskHandlers.test.ts    | 27 ++++++++--
 packages/dashboard/app/hooks/useTaskHandlers.ts    |  8 ++-
 15 files changed, 207 insertions(+), 38 deletions(-)

Fusion-Task-Id: FN-7591

Fusion-Task-Lineage: 510f0e6a-89e7-468f-a6df-ad6aebd5c33a

Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
2026-07-05 13:15:56 -07:00
gsxdsm
09a10bc5e7 FN-7592: replace overseer badge text label with a colored Eye icon
Swaps the planner-overseer status badge from an uppercase text pill to a compact icon glyph, keeping accessibility text on aria-label/title.

- Render a small lucide-react Eye icon instead of the state-label text inside the overseer badge
- Keep the readable state name on aria-label and the composed tooltip on title for accessibility
- Add per-state coloring (watching/steering/recovering/awaiting-confirmation) keyed off the data-planner-overseer-state attribute in TaskCard.css, sized tightly around the icon
- Update TaskCard tests to assert the icon renders and the accessible name moved to aria-label instead of textContent

Files changed:
 packages/dashboard/app/components/TaskCard.css     | 40 ++++++++++++++++++++++
 packages/dashboard/app/components/TaskCard.tsx     | 14 ++++++--
 .../app/components/__tests__/TaskCard.test.tsx     | 25 +++++++++++---
 3 files changed, 72 insertions(+), 7 deletions(-)

Fusion-Task-Id: FN-7592

Fusion-Task-Lineage: d7ca93a6-9236-4fa0-a829-80f5b99dbd5f

Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
2026-07-05 13:11:15 -07:00
gsxdsm
8a7507ad36 FN-7587: add predictive-back slide/fade animation for mobile task-detail dismissal
Adds a presentation-only enter animation for mobile task-detail surfaces (modal and board main-panel), layered on top of the existing FN-7583/FN-7586 dismissal routing, without altering close/back timing.

- Gate a new `.task-detail-modal--mobile-transition` class in TaskDetailModal.tsx via a local resize listener at the 768px breakpoint, mirroring the existing OVERSIGHT_MENU_MOBILE_BREAKPOINT pattern
- Add matching `.task-detail-main-panel--mobile-transition` modifier in MainContent.tsx gated by the existing isMobile prop
- Add slide/fade keyframe animations in TaskDetailModal.css and styles.css, both honoring prefers-reduced-motion
- Add regression tests covering the modal and board-panel mobile transition behavior
- Document the Capacitor WebView limitation preventing a true interactive predictive-back in packages/mobile/README.md

Files changed:
 .../dashboard/app/components/TaskDetailModal.css   |  33 ++
 .../dashboard/app/components/TaskDetailModal.tsx   |  31 +-
 ...skDetail.mobile-transition.board-panel.test.tsx | 333 +++++++++++++++++++++
 .../TaskDetail.mobile-transition.test.tsx          | 156 ++++++++++
 .../app/components/dashboard/MainContent.tsx       |  10 +-
 packages/dashboard/app/styles.css                  |  35 +++
 packages/mobile/README.md                          |  30 ++
 7 files changed, 626 insertions(+), 2 deletions(-)

Fusion-Task-Id: FN-7587

Fusion-Task-Lineage: cc5f08df-4aaf-447d-9c30-237b32191d3f

Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
2026-07-05 12:38:01 -07:00
gsxdsm
da36094d74 FN-7586: add iOS edge-swipe-back gesture parity via AppDelegate patch
Enables the WKWebView interactive-pop (edge-swipe-back) gesture on iOS, matching the Android predictive-back parity already in place, by patching the generated AppDelegate.swift during cap sync.

- Add packages/mobile/scripts/patch-ios-webview.ts: patches AppDelegate.swift to cast the root view controller to CAPBridgeViewController and set webView?.allowsBackForwardNavigationGestures = true before the didFinishLaunchingWithOptions return, is idempotent, and no-ops safely when no ios/ project exists yet
- Wire capacitor:sync:after to run both patch-android-manifest.ts and patch-ios-webview.ts so cap sync keeps both native back-gesture opt-ins in sync; add patch:ios-webview script
- Add unit tests covering patch, idempotency, already-patched, missing-project, and source-preservation cases for the new iOS webview patch, alongside the existing Android manifest patch tests
- Add TaskDetail.swipe-back.test.tsx coverage proving the shared popstate-driven nav-history dismissal stack (no iOS-specific native-back emitter needed) already satisfies the gesture's dismissal contract
- Add mobile-scripts.test.ts dashboard coverage and update MOBILE.md / packages/mobile/README.md documenting the new iOS gesture opt-in

Files changed:
 MOBILE.md                                          |  12 ++
 .../dashboard/app/__tests__/mobile-scripts.test.ts |  31 +++++
 .../__tests__/TaskDetail.swipe-back.test.tsx       | 131 +++++++++++++++++++++
 packages/mobile/README.md                          |  36 ++++++
 packages/mobile/package.json                       |   3 +-
 packages/mobile/scripts/patch-ios-webview.ts       | 119 +++++++++++++++++++
 packages/mobile/src/__tests__/native-shell.test.ts | 122 +++++++++++++++++++
 7 files changed, 453 insertions(+), 1 deletion(-)

Fusion-Task-Id: FN-7586

Fusion-Task-Lineage: 248092a1-de1b-49b6-94ef-70675a7b93a1

Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
2026-07-05 12:24:12 -07:00
gsxdsm
aa6d21eeca FN-7583: route Android back gesture through fusion:native-back so task detail returns to board
Fixes the Android predictive back-gesture (edge swipe) not returning from a task detail view to the board, even though the hardware/legacy Back button worked correctly.

- Patch the generated Android manifest post-sync to set android:enableOnBackInvokedCallback="true" on the <application> tag, opting into AndroidX's OnBackPressedDispatcher for predictive-back gesture completion (mitigating ionic-team/capacitor-plugins#2418 via the disableBackButtonHandler toggle shipped in @capacitor/app@7.1.0).
- Wire the patch script into Capacitor's capacitor:sync:after npm-script hook so it runs automatically after every cap sync (and therefore after cap run android / build:mobile).
- Ensure both the back gesture and the hardware Back button funnel through the same AndroidBackButtonManager backButton listener, dispatching the shared fusion:native-back event consumed by the dashboard's nav-history stack.
- Add regression coverage: a unit test for the manifest patch script's idempotent opt-in behavior, and a dashboard test asserting the task detail view returns to the board on fusion:native-back.
- Document the Android manifest patch rationale and the unchanged dashboard-side invariant in packages/mobile/README.md.

Files changed:
 .../__tests__/TaskDetail.swipe-back.test.tsx       |  13 +++
 packages/mobile/README.md                          |  31 +++++++
 packages/mobile/capacitor.config.ts                |  13 +++
 packages/mobile/package.json                       |   2 +
 packages/mobile/scripts/patch-android-manifest.ts  |  90 ++++++++++++++++++
 packages/mobile/src/__tests__/native-shell.test.ts | 102 +++++++++++++++++++++
 6 files changed, 251 insertions(+)

Fusion-Task-Id: FN-7583

Fusion-Task-Lineage: ad0cb02c-6e49-448a-8c93-607cd5ae657b

Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
2026-07-05 11:57:36 -07:00
gsxdsm
0f1cd0a36a FN-7585: unify border, radius, and height across task-detail Priority/Execution-mode/Oversight controls
Unifies the visual styling of the task-detail modal's Priority, Execution-mode, and Oversight quick-control chips so the cluster reads as one consistent control group.

- Add a shared --detail-control-border-radius token (resolving to --radius-md) alongside the existing --detail-priority-control-min-height token
- Override .detail-priority-chip's inherited transparent border with a visible --btn-border-width/--border pairing so the "normal" priority level renders as a bordered box instead of borderless text
- Pin the same border-width/color/radius trio on .detail-execution-mode-toggle so a future change to .btn defaults can't desync the cluster
- Apply the same trio to .detail-oversight-chip, overriding .card-oversight-badge's transparent border (covers the neutral "off" tint too)
- Apply the same trio to the mobile .detail-oversight-menu-trigger swap-in so the mobile overflow-trigger variant matches the desktop chip
- Add a changeset (patch) documenting the fix for @runfusion/fusion
- Extend TaskDetailModal.responsive-and-dependencies.test.tsx coverage for the unified styling

Files changed:
 .changeset/FN-7585-unify-task-detail-quick-control-styling.md                         |  7 +++
 packages/dashboard/app/components/TaskDetailModal.css                                 | 55 ++++++++++++++++++++++
 packages/dashboard/app/components/__tests__/TaskDetailModal.responsive-and-dependencies.test.tsx | 27 +++++++++++
 3 files changed, 89 insertions(+)

Fusion-Task-Id: FN-7585

Fusion-Task-Lineage: 0cca8c82-c9fb-4410-af48-51861a743f96

Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
2026-07-05 11:49:42 -07:00
fusion-merge-train
15c2c831a8 fix(engine): self-heal failed in-review cards whose PR merged on the remote
A transient error at merge time can flag an in-review card `failed` even
when its PR actually squash-merged on the remote. `recoverAlreadyMergedReviewTasks`
only ran the already-merged evidence detector against the LOCAL base ref, so
when this process never fetched the merge, the owned commit was absent locally,
the detector returned null, and the card held its file-scope lease forever.

Fetch-then-prove: when a failed candidate has a recorded PR and the local base
yields no owned commit, best-effort `git fetch origin <base>` and re-run the
SAME evidence detector against `origin/<base>`. The owned-commit proof and every
foreign-ownership guard inside the detector remain the sole finalize gate, so
this only un-wedges a genuinely-merged task — it never phantom-finalizes on
unproven state. Gated on a recorded PR (no PR ⇒ nothing merged remotely ⇒ no
fetch); fail-closed on fetch error.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-05 20:41:20 +02:00
gsxdsm
72b77bf621 fix(FN-7561): stop Plan Review replan loop and fix "can't find the plan" reviews
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>
2026-07-05 11:31:49 -07:00
gsxdsm
a1a6b09a4f FN-7582: clarify oversight nudge-disabled guideline copy
Reword the disabled-Nudge tooltip/helper text so it no longer reads as an overseer fault, and split it into two distinct reasons.
- Add taskDetail.oversight.nudgeSuppressedTitle for the human-control-suppressed case (user-paused, done/archived, autoMerge:false human-review terminal), naming manual control as the cause
- Reword taskDetail.oversight.nudgeDisabledTitle to a reassuring periodic-poll framing for the no-observation-yet case instead of implying the overseer is idle
- Compute the shared nudgeDisabledReason once and reuse it at all four render sites (mobile menu + desktop inline title/helper) so the two copies can't drift
- Add a changeset (patch) documenting the operator-facing copy fix
- Extend TaskDetailModal.oversight-controls tests and test-helpers to cover the new suppressed-vs-disabled copy branching

Files changed:
 .changeset/fn-7582-oversight-guideline-copy.md     |  7 ++
 .../dashboard/app/components/TaskDetailModal.tsx   | 37 +++++++++--
 .../TaskDetailModal.oversight-controls.test.tsx    | 76 +++++++++++++++++++++-
 .../__tests__/TaskDetailModal.test-helpers.ts      |  7 ++
 4 files changed, 121 insertions(+), 6 deletions(-)

Fusion-Task-Id: FN-7582

Fusion-Task-Lineage: bf2ceab4-2fb0-4686-a9e1-9e015502a521

Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
2026-07-05 11:31:49 -07:00
gsxdsm
53fe0d71b9 FN-7584: register builtin:brainstorming workflow with ask-user/exit-gate loop
Adds a discoverable built-in Brainstorming workflow composing the ask-user + exit-gate reach-out loop ahead of the standard coding plan/execute/review/merge spine, plus a WorkflowNodeEditor fix so clearing the ask-user question textarea deletes the config key instead of persisting an empty string.

- Add packages/core/src/builtin-brainstorming-workflow-ir.ts registering builtin:brainstorming (non-default, default-enabled): ask-user -> refine prompt -> exit-gate-on-approval ahead of the unmodified Coding plan/execute/review/merge spine
- Wire the new builtin into packages/core/src/builtin-workflows.ts and extend the builtin-workflows parity test suite
- Add builtin-brainstorming-workflow-ir.test.ts covering the new workflow's IR shape and validation
- Fix WorkflowNodeEditor.tsx ask-user question textarea onChange to delete the config.question key when cleared to empty (validateAskUserAndExitGateNodes rejects present-but-empty question; only an absent key falls back to the engine default)
- Update docs/workflow-steps.md to document builtin:brainstorming as a selectable built-in composition
- Add .changeset/fn-7584-brainstorming-builtin.md (minor, feature)

Files changed:
 .changeset/fn-7584-brainstorming-builtin.md        |   7 ++
 docs/workflow-steps.md                             |   2 +-
 .../builtin-brainstorming-workflow-ir.test.ts      |  79 ++++++++++++++++
 .../core/src/__tests__/builtin-workflows.test.ts   |  66 +++++++++++++
 .../core/src/builtin-brainstorming-workflow-ir.ts  | 103 +++++++++++++++++++++
 packages/core/src/builtin-workflows.ts             |  46 +++++++++
 .../app/components/WorkflowNodeEditor.tsx          |  18 +++-
 7 files changed, 319 insertions(+), 2 deletions(-)

Fusion-Task-Id: FN-7584

Fusion-Task-Lineage: 2c0258c2-9a35-403a-8688-ee49393a7231

Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
2026-07-05 11:31:48 -07:00
gsxdsm
ab28e7811c FN-7581: make Intervention Timeline activity panel full width on mobile
Stop the Interventions Activity segment from reserving padding for an overlay toggle button it never renders, which was insetting the FN-7519 timeline from the right edge (worse on mobile).

- Add `.detail-activity--interventions` CSS modifier that zeroes `padding-inline-end` for the Interventions segment, both base and mobile breakpoints, while leaving Live/Feed/Raw's reserved padding untouched
- Apply the new modifier class to the Interventions `.detail-activity` container in TaskDetailModal.tsx
- Add a regression test asserting the Interventions container carries `detail-activity--interventions` while the Feed container (which still renders the overlay toggle) does not

Files changed:
 .../dashboard/app/components/TaskDetailModal.css   | 18 ++++++++++
 .../dashboard/app/components/TaskDetailModal.tsx   |  2 +-
 .../TaskDetailModal.oversight-controls.test.tsx    | 38 ++++++++++++++++++++++
 3 files changed, 57 insertions(+), 1 deletion(-)

Fusion-Task-Id: FN-7581

Fusion-Task-Lineage: d5c55773-7df3-4eb4-aa70-01f6e0a622c6

Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
2026-07-05 11:31:48 -07:00
gsxdsm
42bbe58c03 FN-7579: add ask-user and exit-gate workflow nodes
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>
2026-07-05 11:31:48 -07:00
gsxdsm
74358494a7 FN-7577: extend PR-based revert to workspace tasks under autoMerge:false
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>
2026-07-05 11:31:48 -07:00
gsxdsm
b173f76adb fix(FN-7577): stop planner overseer from "recovering" healthy in-progress tasks
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>
2026-07-05 11:31:48 -07:00
gsxdsm
78d4db94d8 FN-7575: add release version lines to Fusion self-repo done comments
Extends GitHubIssueCommentService so task-close comments on issues in the runfusion/fusion repo itself append both a current-version and target-next-minor-release line, while comments on all other linked repos stay byte-for-byte unchanged.

- Add isFusionSelfRepo() and computeNextMinorVersion() helpers to github-issue-comment.ts
- Append "Current version: v{current}" and "Target release: v{next-minor}" lines only when the linked source issue's repo is runfusion/fusion (case-insensitive)
- Fall back silently (no version lines) when the resolved version is unparseable or the unresolved 0.0.0 sentinel
- Add changeset (minor) documenting the new behavior
- Update docs/settings-reference.md and docs/gitlab-parity-inventory.md
- Expand github-issue-comment.test.ts coverage for self-repo vs other-repo behavior and version edge cases

Files changed:
 .changeset/fn-7575-release-version-comment.md      |   7 +
 docs/gitlab-parity-inventory.md                    |   2 +-
 docs/settings-reference.md                         |   2 +-
 packages/dashboard/src/__tests__/github-issue-comment.test.ts | 142 ++++++++++++++++++++-
 packages/dashboard/src/github-issue-comment.ts     |  69 +++++++++-
 5 files changed, 212 insertions(+), 10 deletions(-)

Fusion-Task-Id: FN-7575

Fusion-Task-Lineage: b7cf7e6f-8d96-4442-8595-5d54ea911481

Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
2026-07-05 11:31:48 -07:00
gsxdsm
45e5a269e8 fix(FN-7579): stop tracking-issue dedup from linking new tasks to stale issues
GitHub tracking-issue creation reused old/closed issues via dedup:
- searched state:"all", so a resolved issue from an unrelated task could be reused
- accepted keyword-only matches (generic shared identifiers) with no file overlap

Dedup now only reuses OPEN issues and requires a File-Scope path overlap;
without an open path-overlapping issue a fresh tracking issue is always created.
Adds two regression tests asserting closed and keyword-only matches are not reused.

Fusion-Task-Id: FN-7579

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-05 11:31:48 -07:00
gsxdsm
9a0951bf27 FN-7580: Backfill ~90 missing i18n keys across locale catalogs
Fills in translation keys that were missing (empty-string placeholders) in the es, fr, ko, zh-CN, and zh-TW locale app.json catalogs, keeping parity with the en source catalog.

- Added missing settings/workflow keys (aiUndoTaskWorkflow, aiUndoTaskWorkflowInherit, aiUndoTaskWorkflowHelp, workspaceModeHint, allowAbsoluteFileBrowserPathsHint, quickChatLauncherHint, showTaskChatsInCommonFeedHint, whenEnabledImportedGitHubIssuesUseTheirSource, gitLabEnabledHint, allowEphemeralAgentsToCreateTasksHint, quickChatCloseOnOutsideClickHint, disabledFusionWorkflowsAreHiddenFromWorkflow) to es/fr/ko/zh-CN/zh-TW app.json.
- Resolved a merge conflict where main had independently added aiUndoTaskWorkflow* keys to the same object; merged both key sets without duplication or data loss.
- Verified i18n key parity across all 5 secondary locales / 4 namespaces via check-i18n-parity.mjs.

Files changed:
 packages/i18n/locales/es/app.json    | 159 ++++++++++++++++++++++++++++++-----
 packages/i18n/locales/fr/app.json    | 159 ++++++++++++++++++++++++++++++-----
 packages/i18n/locales/ko/app.json    | 159 ++++++++++++++++++++++++++++++-----
 packages/i18n/locales/zh-CN/app.json | 148 +++++++++++++++++++++++++++-----
 packages/i18n/locales/zh-TW/app.json | 159 ++++++++++++++++++++++++++++++-----
 5 files changed, 679 insertions(+), 105 deletions(-)

Fusion-Task-Id: FN-7580

Fusion-Task-Lineage: 78684020-6085-4adb-8e9b-7b35d7448edd

Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
2026-07-05 11:31:48 -07:00
gsxdsm
73b38babf0 FN-7578: surface aiUndoTaskWorkflowId picker in Settings General
Adds a Settings → General picker so operators can choose which workflow governs AI-undo (revert) board tasks, instead of it being fixed.

- Add aiUndoTaskWorkflowId select to GeneralSection, defaulting to builtin:review-heavy, with an "Inherit project default workflow" empty-string option matching the revert route's blank-is-inherit behavior (FN-7556)
- Load full workflow list (including custom workflows, excluding fragments) separately from the builtin-only workflow list used for enable/disable checkboxes
- Add FNXC:TaskRevert comment documenting the default/inherit semantics
- Add tests for the new picker and update settings-default-descriptions test
- Add changeset (minor) and update settings-reference/task-management docs
- Add i18n strings across en/es/fr/ko/zh-CN/zh-TW locales

Files changed:
 .changeset/fn-7578-ai-undo-workflow-setting-ui.md  |   7 ++
 docs/settings-reference.md                         |   2 +-
 docs/task-management.md                            |   2 +-
 .../settings/sections/GeneralSection.tsx           |  42 +++++++
 .../GeneralSection.aiUndoWorkflow.test.tsx         | 129 +++++++++++++++++++++
 .../settings-default-descriptions.test.tsx         |   3 +-
 packages/i18n/locales/en/app.json                  |   5 +-
 packages/i18n/locales/es/app.json                  |   5 +-
 packages/i18n/locales/fr/app.json                  |   5 +-
 packages/i18n/locales/ko/app.json                  |   5 +-
 packages/i18n/locales/zh-CN/app.json               |   5 +-
 packages/i18n/locales/zh-TW/app.json               |   5 +-
 12 files changed, 205 insertions(+), 10 deletions(-)

Fusion-Task-Id: FN-7578
Fusion-Task-Lineage: 1d1701ab-ecc7-4bbe-a003-2ce45ac15a25
Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
2026-07-05 11:31:47 -07:00
gsxdsm
e7cb2f1fed FN-7525: add Revert/Undo affordance to Done and Archived task cards
Adds a Revert action so operators can undo landed changes for Done/Archived tasks directly from the board.

- Adds onRevertTask wiring through Board, Column, Lane, ListView, TaskDetailModal, and WorktreeGroup surfaces
- Adds a Revert affordance to TaskCard (Done/Archived states) with confirm UX and CSS
- Adds POST /tasks/:id/revert legacy API route supporting "auto" mode with an AI-undo fallback (mode: "ai") on conflict
- Wires useTasks hook and dashboard MainContent/types to support the new revert action
- Adds new i18n strings for the revert affordance
- Adds a minor changeset for @runfusion/fusion documenting the feature
- Adds/updates tests: TaskCard.test.tsx, board-mobile.test.tsx, api-git.test.ts
- Updates docs/dashboard-guide.md and docs/task-management.md

Files changed:
 .changeset/fn-7525-revert-card-affordance.md       |   7 +
 docs/dashboard-guide.md                            |   2 +-
 docs/task-management.md                            |  10 ++
 packages/dashboard/app/App.tsx                     |   7 +-
 packages/dashboard/app/__tests__/api-git.test.ts   |  51 +++++++
 packages/dashboard/app/api/legacy.ts               |  51 +++++++
 packages/dashboard/app/components/AppModals.tsx    |   5 +-
 packages/dashboard/app/components/Board.tsx        |  10 +-
 packages/dashboard/app/components/Column.tsx       |   8 +-
 packages/dashboard/app/components/Lane.tsx         |   5 +-
 packages/dashboard/app/components/ListView.tsx     |  80 ++++++++++-
 packages/dashboard/app/components/TaskCard.css     |  24 +++-
 packages/dashboard/app/components/TaskCard.tsx     | 127 ++++++++++++++++-
 packages/dashboard/app/components/TaskDetailModal.tsx   |  85 ++++++++++++
 packages/dashboard/app/components/WorktreeGroup.tsx     |   6 +
 packages/dashboard/app/components/__tests__/TaskCard.test.tsx     | 151 +++++++++++++++++++++
 packages/dashboard/app/components/__tests__/board-mobile.test.tsx |  63 +++++++++
 packages/dashboard/app/components/dashboard/MainContent.tsx       |   4 +
 packages/dashboard/app/components/dashboard/types.ts    |   8 ++
 packages/dashboard/app/components/useRightDockController.tsx      |   4 +
 packages/dashboard/app/hooks/useTasks.ts           |  21 ++-
 packages/i18n/locales/en/app.json                  |  11 ++
 22 files changed, 718 insertions(+), 22 deletions(-)

Fusion-Task-Id: FN-7525

Fusion-Task-Lineage: b9e2ca94-4ec6-4443-8e41-cf4828018856

Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
2026-07-05 11:31:47 -07:00
gsxdsm
94e9d15e38 FN-7556: auto-select review-heavy workflow for AI-undo tasks
AI-undo tasks now default to a configurable, stricter review workflow instead of always inheriting the project default.

- Add project setting `aiUndoTaskWorkflowId` (default `builtin:review-heavy`) to ProjectSettings type and DEFAULT_PROJECT_SETTINGS
- `POST /api/tasks/:id/revert` resolves and validates the configured workflow id (via `isBuiltinWorkflowId`/`getWorkflowDefinition`), falling back to inherit-with-warning on a blank/unknown value
- `createAiUndoTask` engine helper gains an optional `workflowId` param, forwarded verbatim to `createTask` only when non-blank, staying pure (no settings/store access itself)
- Add regression tests for the route resolution logic and the engine helper's workflow forwarding
- Update docs (`settings-reference.md`, `task-management.md`) and add changeset

Files changed:
 .changeset/fn-7556-ai-undo-workflow.md             |  7 +++
 docs/settings-reference.md                         |  1 +
 docs/task-management.md                            |  1 +
 packages/core/src/settings-schema.ts               |  4 ++
 packages/core/src/types.ts                         | 14 +++++
 .../settings-default-descriptions.test.tsx         |  2 +
 .../src/__tests__/task-revert-route.test.ts        | 52 ++++++++++++++++-
 .../src/routes/register-task-workflow-routes.ts    | 31 +++++++++-
 .../src/__tests__/task-revert-ai-undo.test.ts      | 67 ++++++++++++++++++++++
 packages/engine/src/task-revert.ts                 | 16 ++++++
 10 files changed, 192 insertions(+), 3 deletions(-)

Fusion-Task-Id: FN-7556
Fusion-Task-Lineage: dec5603c-10a8-4780-a1b8-8a836a0de4c1
Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
2026-07-05 11:31:47 -07:00
gsxdsm
2df6c356fc FN-7554: add PR-based revert path for autoMerge:false projects
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>
2026-07-05 11:31:47 -07:00
gsxdsm
a5ac3c3314 fix(FN-7566): stop phantom-binding reclaim from killing live ephemeral executor tasks
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>
2026-07-05 11:31:47 -07:00
gsxdsm
4b530a65de fix: restore Anthropic subscription card after in-session logout + re-login
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>
2026-07-05 11:31:47 -07:00
gsxdsm
b471aece6a fix(FN-7560): stop release-auth gate flagging tasks that disclaim releasing
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>
2026-07-05 11:31:47 -07:00
gsxdsm
196abb5af9 FN-7576: delegate anthropic-subscription getApiKey to engine authStorage
Reads for the anthropic-subscription provider now delegate to the real engine authStorage so expired OAuth tokens are refreshed instead of silently failing.

- mergeAuthStorageReads getApiKey("anthropic-subscription") now calls target.getApiKey(providerId) directly, triggering the engine's refresh-token HTTP round trip, instead of a local static Date.now() >= credential.expires check
- Falls back to the read-only fallback storages' local resolution only when the primary engine call yields no key
- Added regression tests exercising the wrapper directly against the engine delegation and fallback behavior
- Added a patch changeset documenting the fix for @runfusion/fusion

Files changed:
 .changeset/fn-7576-cli-wrapper-subscription-getapikey-delegation.md               |   7 +
 packages/cli/src/commands/__tests__/provider-auth.test.ts                         | 148 +++++++++++++++++++++
 packages/cli/src/commands/provider-auth.ts                                        |  12 +-
 3 files changed, 166 insertions(+), 1 deletion(-)

Fusion-Task-Id: FN-7576

Fusion-Task-Lineage: 31e495ec-8487-468b-9b80-36e8d0f53806

Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
2026-07-05 11:31:46 -07:00
gsxdsm
ce9df297eb FN-7574: fix OAuth token expiry detection and add proactive auto-refresh
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>
2026-07-05 11:31:46 -07:00
gsxdsm
92ba45cc01 fix: normalize Anthropic subscription callback URLs 2026-07-05 11:31:46 -07:00
gsxdsm
986e249bc1 FN-7555: surface bidirectional undo/source task links in dashboard UI
Adds a client-side helper and UI affordances so AI-undo tasks and their source tasks link to each other in the dashboard, without any new API or backend changes.

- Add packages/dashboard/app/utils/taskRevert.ts with getRevertOfId() (resolves the revertOf marker written by createAiUndoTask, with a defensive sourceType==="recovery" fallback to sourceParentTaskId) and findOpenUndoTaskForSource() (reverse lookup for the most recent open undo task pointing at a source task)
- TaskCard and TaskDetailModal now render "Undo of <id>" / "Undo task: <id>" provenance links using the shared helper, keeping forward and reverse affordances consistent
- Add TaskCard.css styling for the new undo-link affordance
- Add/extend tests: TaskCard.test.tsx and TaskDetailModal.rendering.test.tsx cover the new undo/source link rendering
- Update docs/task-management.md and packages/i18n/locales/en/app.json for the new UI copy

Files changed:
 docs/task-management.md                            |   2 +-
 packages/dashboard/app/components/TaskCard.css     |  37 +++++
 packages/dashboard/app/components/TaskCard.tsx     |  29 +++-
 .../dashboard/app/components/TaskDetailModal.tsx   |  46 ++++++
 .../app/components/__tests__/TaskCard.test.tsx     |  46 ++++++
 .../__tests__/TaskDetailModal.rendering.test.tsx   | 160 +++++++++++++++++++++
 packages/dashboard/app/utils/taskRevert.ts         |  80 +++++++++++
 packages/i18n/locales/en/app.json                  |   7 +-
 8 files changed, 403 insertions(+), 4 deletions(-)

Fusion-Task-Id: FN-7555

Fusion-Task-Lineage: c3d2dbd2-4d86-40b5-afce-0ca7a6dfc73d

Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
2026-07-05 11:31:46 -07:00