Commit Graph

10763 Commits

Author SHA1 Message Date
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
670c41345b chore: drop duplicate changeset for the OAuth refresh scope fix
Both changesets in ed823c794 describe the same fix; keep the more complete one.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-05 18:50:37 -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
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
c0abca90d8 FN-7598: add planner-oversight discovery pointers to README and docs hub
Docs-only change adding front-door discovery for the already-shipped planner-oversight feature (FN-7508 → FN-7583), which previously had no entry point outside internal reference docs.

- Add a README.md feature table row and a new "Planner oversight" section describing oversight levels (off/observe/steer/autonomous) and the always-on human-confirmation gate for merge/PR and destructive actions, linking to Settings Reference and Dashboard Guide
- Add a README.md capabilities bullet cross-linking the new section
- Add a docs/README.md hub row pointing to Settings Reference, Dashboard Guide, and Architecture for planner oversight, and extend the 'power user' reading path
- Add a one-line pointer in docs/getting-started.md workflow section noting per-task/workflow oversight controls

Files changed:
 README.md               | 11 +++++++++++
 docs/README.md          |  9 ++++++---
 docs/getting-started.md |  3 +++
 3 files changed, 20 insertions(+), 3 deletions(-)

Fusion-Task-Id: FN-7598

Fusion-Task-Lineage: 8141b44c-f007-45c0-a057-f4eeb34ae8d4

Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
2026-07-05 16:51:05 -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
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
gsxdsm
7c0be5311a FN-7567: re-bake xterm letter-spacing after fit() to fix mobile terminal spacing recurrence
Fixes recurrence #4 of the mobile terminal excess character-spacing bug: SessionTerminal and TerminalModal now force a second genuine xterm font remeasure AFTER fitAddon.fit() settles the post-fit column count, since handleResize() never re-bakes DomRenderer's letter-spacing compensation itself.

- SessionTerminal.tsx: call forceTerminalFontRemeasure() again after fit()/sendResizeMessage() in the resize handler, re-baking spacing against the settled (post-fit) column count instead of the stale pre-fit one.
- TerminalModal.tsx: same second forceTerminalFontRemeasure() call after fitAddon.fit()/sendResize() in its resize handling path.
- Expanded SessionTerminal.test.tsx and TerminalModal.test.tsx coverage to assert the post-fit remeasure occurs.
- Added docs/solutions/ui-bugs/xterm-options-noop-remeasure-after-font-settle.md documenting recurrence #4 root cause (DomRenderer._setDefaultSpacing() never recomputes from handleResize()).
- Added changeset fn-7567-mobile-terminal-spacing.md (patch, category fix).

Files changed:
 .changeset/fn-7567-mobile-terminal-spacing.md      |   7 +
 docs/solutions/ui-bugs/xterm-options-noop-remeasure-after-font-settle.md |  89 ++++++
 packages/dashboard/app/components/SessionTerminal.tsx   |  23 ++
 packages/dashboard/app/components/TerminalModal.tsx     |  43 ++-
 packages/dashboard/app/components/__tests__/SessionTerminal.test.tsx  | 127 +++++++-
 packages/dashboard/app/components/__tests__/TerminalModal.test.tsx    | 334 ++++++++++++++++++++-
 6 files changed, 619 insertions(+), 4 deletions(-)

Fusion-Task-Id: FN-7567
Fusion-Task-Lineage: 5da20522-82d3-4c4d-9008-db71bc5b4d75
Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
2026-07-05 11:31:46 -07:00
gsxdsm
9592e3ac5c FN-7569: skip re-asking manual plan approval for unchanged re-specified plans
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>
2026-07-05 11:31:46 -07:00
gsxdsm
c31f9ef4d4 FN-7571: move planner intervention timeline into Activity dropdown
Relocates the FN-7519 Intervention Timeline out of the inline oversight cluster and into the task Activity view as a fourth selectable segment.
- Add `interventions` to the `ActivitySegment` union and gate its dropdown option on the same oversight-active expression previously used by the inline mount
- Render `PlannerInterventionTimeline` in a new Activity segment panel instead of inline next to the oversight controls
- Fall back to the Live segment automatically if oversight turns off (or was never resolved) while Interventions is selected, avoiding an orphaned selection
- Update oversight-controls and task-activity-chat tests to cover the new dropdown segment and removed inline mount
- Add changeset and update dashboard guide docs

Files changed:
 ...7571-intervention-timeline-activity-dropdown.md |   7 +
 docs/dashboard-guide.md                            |   4 +-
 .../dashboard/app/components/TaskDetailModal.tsx   |  49 ++++---
 .../TaskDetailModal.oversight-controls.test.tsx    | 148 +++++++++++++++++++++
 .../TaskDetailModal.task-activity-chat.test.tsx    |   7 +-
 5 files changed, 195 insertions(+), 20 deletions(-)

Fusion-Task-Id: FN-7571

Fusion-Task-Lineage: 331a1b32-5712-4583-9777-e36ff5cc3ffc

Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
2026-07-05 11:31:45 -07:00
gsxdsm
bc0553a949 FN-7573: shorten oversight-controls changeset summary under 120 chars
Shortened the changeset summary line for the oversight Nudge/Explain controls fix so it stays within the 120-character release-notes limit.

- Replaced the overlong summary with a concise version describing the visible label, disabled reason, and always-openable Explain panel

Files changed:
 .changeset/FN-7546-oversight-controls-clarity.md | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

Fusion-Task-Id: FN-7573

Fusion-Task-Lineage: e7b429db-06cd-4a11-b758-49d37a1ff7c2

Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
2026-07-05 11:31:45 -07:00
gsxdsm
a8865aa058 FN-7572: shorten mobile-terminal-footer changeset summary under 120 chars
Narrative: The FN-7560 changeset summary line exceeded the 120-char changeset linter limit; trimmed wording while preserving the user-facing meaning.

- Shortened the `summary` field in .changeset/fn-7560-mobile-terminal-footer.md to fit within the 120-character limit enforced by pnpm check:changesets

Files changed:
 .changeset/fn-7560-mobile-terminal-footer.md | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

Fusion-Task-Id: FN-7572

Fusion-Task-Lineage: 96e2b047-0c47-4717-b870-6df8d90bbd92

Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
2026-07-05 11:31:45 -07:00
gsxdsm
fb45157401 FN-7565: pin mobile terminal close button to top-right corner
Corner-pins the mobile terminal header's close (X) button so it renders last in flex order and hugs the top-right edge, instead of falling back to order:0 and landing at the far left of the header.

- Add .terminal-close--corner class (order:3 + margin-inline-start:auto) applied only on mobile, both in the base .terminal-modal--mobile selector and the max-width:768px media query (covers folded-Android visualViewport path)
- Apply terminal-close--corner alongside terminal-close on the mobile close button in TerminalModal.tsx; desktop/floating/pinned-below placement inside .terminal-actions is unchanged
- Add regression tests asserting the corner-pin class, its persistence across connection/exit states, the unaffected desktop .terminal-actions placement, and the CSS flex-order relationship vs .terminal-mobile-tabs/.terminal-workspace-picker
- Add changeset (patch) documenting the fix for release notes

Files changed:
 .changeset/fn-7565-mobile-terminal-close-corner.md |   7 ++
 .../dashboard/app/components/TerminalModal.css     |  30 +++++
 .../dashboard/app/components/TerminalModal.tsx     |  14 ++-
 .../components/__tests__/TerminalModal.test.tsx    | 126 +++++++++++++++++++++
 4 files changed, 176 insertions(+), 1 deletion(-)

Fusion-Task-Id: FN-7565
Fusion-Task-Lineage: f5d82128-6c14-4281-9cfc-fd87dbc89d5d
Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
2026-07-05 11:31:45 -07:00
gsxdsm
34fd3c7912 FN-7570: shorten oversight-controls changeset summary to fit 120-char limit
Trims the FN-7546 changeset summary line so it stays within the 120-character release-notes limit.
- Shortened the `summary:` field in .changeset/FN-7546-oversight-controls-clarity.md
- Preserved category and dev fields unchanged

Files changed:
 .changeset/FN-7546-oversight-controls-clarity.md | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

Fusion-Task-Id: FN-7570

Fusion-Task-Lineage: c486f2af-9543-4976-8b24-ee4bd3a2d07d

Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
2026-07-05 11:31:45 -07:00
gsxdsm
24b27e8b38 FN-7564: block approve/reject-plan API bypass of release-authorization holds
Enforce the FN-7559 release-authorization hold in the approve-plan and reject-plan routes, since a direct API call previously bypassed the dashboard's UI-only protection.

- Add a guard in register-task-workflow-routes.ts: reject approve-plan and reject-plan requests with 400 when task.awaitingApprovalReason === "release-authorization", instructing the caller to add the **Release Authorized By User:** yes marker instead.
- Add regression tests in routes-github.test.ts covering both approve-plan and reject-plan against release-authorization-held tasks.
- Add changeset fn-7564-approve-plan-release-authorization-guard.md (patch, security) documenting the fix.

Files changed:
 .changeset/fn-7564-approve-plan-release-authorization-guard.md | 7 ++
 packages/dashboard/src/__tests__/routes-github.test.ts | 82 ++++++++++++++++++++++
 packages/dashboard/src/routes/register-task-workflow-routes.ts | 23 ++++++
 3 files changed, 112 insertions(+)

Fusion-Task-Id: FN-7564

Fusion-Task-Lineage: ccadd492-ee2b-4f0e-af2f-5f37c6351922

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