Commit Graph

11628 Commits

Author SHA1 Message Date
gsxdsm
67fac31f5c FN-8202: prevent mDNS hostname conflicts
Prevent self-conflicting mDNS advertisements and allow automatic LAN discovery to be disabled.

- Advertise a Fusion-owned DNS-SD hostname derived from the node ID.
- Add a global automatic LAN discovery opt-out for dashboard and serve.
- Cover hostname isolation and discovery startup behavior with tests and documentation.

Files changed:
 .changeset/fn-8202-mdns-hostname-fix.md            |  7 +++++
 docs/settings-reference.md                         |  1 +
 docs/shared-mesh-protocol.md                       |  2 +-
 .../cli/src/commands/__tests__/dashboard.test.ts   | 24 ++++++++++++++++
 packages/cli/src/commands/__tests__/serve.test.ts  | 12 ++++++++
 packages/cli/src/commands/dashboard.ts             | 24 +++++++++++-----
 packages/cli/src/commands/serve.ts                 | 24 +++++++++++-----
 packages/core/src/__tests__/node-discovery.test.ts | 32 +++++++++++++++++++++-
 .../core/src/__tests__/settings-defaults.test.ts   |  8 ++++++
 packages/core/src/node-discovery.ts                | 31 +++++++++++++++++++++
 packages/core/src/settings-schema.ts               |  5 ++++
 packages/core/src/types.ts                         |  2 ++
 12 files changed, 156 insertions(+), 16 deletions(-)

Fusion-Task-Id: FN-8202

Fusion-Task-Lineage: c1d6e36b-bda7-4968-b40c-c14e16ebda28

Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
2026-07-17 09:54:44 -07:00
gsxdsm
7fc4b439ee FN-8205: fix shared auth credential lock retries
Serialize shared auth credential writes and await durable persistence under contention.

- Replace synchronous auth-file locking with queued asynchronous retries.
- Propagate asynchronous credential mutations through CLI, dashboard, and provider wrappers.
- Cover held-lock persistence, queue recovery, and awaited caller behavior.

Files changed:
 .changeset/fn-8205-auth-lock-retry.md              |   7 ++
 .../cli/src/commands/__tests__/onboard.test.ts     |  17 +++
 .../src/commands/__tests__/provider-auth.test.ts   |  78 ++++++------
 packages/cli/src/commands/onboard.ts               |   2 +-
 packages/dashboard/src/routes.ts                   |   6 +-
 .../dashboard/src/routes/register-auth-routes.ts   |  10 +-
 .../dashboard/src/routes/register-mesh-routes.ts   |   4 +-
 .../register-settings-sync-inbound-routes.ts       |   4 +-
 .../src/routes/register-settings-sync-routes.ts    |   4 +-
 .../src/__tests__/auth-storage-concurrency.test.ts |  84 ++++++++++---
 packages/engine/src/__tests__/auth-storage.test.ts |  48 ++++----
 packages/engine/src/auth-storage.ts                | 137 +++++++++++++--------
 packages/engine/src/provider-auth.ts               |  56 ++++-----
 13 files changed, 284 insertions(+), 173 deletions(-)

Fusion-Task-Id: FN-8205

Fusion-Task-Lineage: b932b9ce-9aee-4f0a-9155-63b026c6bde4

Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
2026-07-17 09:47:40 -07:00
gsxdsm
4293826b5a FN-8212: remove model dropdown sticky-header gap
Make pinned model provider headers sit flush against the fixed dropdown header stack.

- Remove top padding that exposed scrolling rows above sticky provider headers.
- Cover the no-seam invariant across desktop and mobile layouts with and without thinking controls.
- Add a patch changeset for the dropdown scrolling fix.

Files changed:
 .changeset/fn-8212-model-dropdown-sticky-gap.md    |  7 ++++
 .../app/components/CustomModelDropdown.css         |  5 ++-
 .../__tests__/CustomModelDropdown.test.tsx         | 46 ++++++++++++++++++++++
 3 files changed, 57 insertions(+), 1 deletion(-)

Fusion-Task-Id: FN-8212

Fusion-Task-Lineage: d7ce42d1-bcbe-4000-8107-794ec8e05307

Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
2026-07-17 09:35:14 -07:00
gsxdsm
d6860b5b76 FN-8204: add configurable mobile footer quick actions
Add an ordered project setting for mobile footer quick-action destinations.

- Render selected destinations as primary mobile tabs and retain omitted choices in More.
- Provide settings controls, validation, translations, documentation, and regression coverage.
- Add a minor changeset for the published CLI package.

Files changed:
 .changeset/fn-8204-mobile-nav-primary-items.md     |   7 +
 docs/dashboard-guide.md                            |   4 +
 docs/settings-reference.md                         |   4 +
 .../src/__tests__/mobile-nav-primary-items.test.ts |  23 +++
 packages/core/src/index.ts                         |   9 ++
 packages/core/src/mobile-nav-primary-items.ts      |  55 +++++++
 packages/core/src/settings-schema.ts               |   1 +
 packages/core/src/types.ts                         |   7 +
 packages/dashboard/app/App.tsx                     |   2 +
 .../mobile-feature-access-regression.test.tsx      |  19 +++
 packages/dashboard/app/components/MobileNavBar.tsx | 159 +++++----------------
 .../app/components/__tests__/MobileNavBar.test.tsx |  16 +++
 .../app/components/settings/section-keys.ts        |   1 +
 .../settings/sections/GeneralSection.search.ts     |   9 ++
 .../settings/sections/GeneralSection.tsx           |  45 ++++++
 .../settings-default-descriptions.test.tsx         |   1 +
 packages/dashboard/app/hooks/useAppSettings.ts     |   5 +
 packages/i18n/locales/en/app.json                  |   4 +
 18 files changed, 244 insertions(+), 127 deletions(-)

Fusion-Task-Id: FN-8204

Fusion-Task-Lineage: 077de4c4-d281-401d-bddd-e5ce8e91d4ab

Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
2026-07-17 09:27:33 -07:00
gsxdsm
c0d610d9d7 fix(whatsapp-plugin): connect with current WA Web version and load as bundled plugin
WhatsApp rejects handshakes advertising Baileys' baked-in stale protocol
version with a 405 close, so the plugin cycled starting->disconnected and
never issued a QR. connect() now fetches the current WA Web version per
socket build. /status also exposes lastError so this failure mode is
diagnosable from the documented troubleshooting surface.

The published bundled.js also failed to load entirely ("Dynamic require
of 'crypto' is not supported"): plugin bundles are ESM but Baileys is
CJS. bundlePluginEntry now injects the same createRequire banner as
dist/bin.js.

Verified end-to-end against an isolated fn serve: bundled.js loads,
status reaches awaiting-qr, /qr serves a scannable QR data URL,
/pair-code validates input, /logout clears auth state.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-17 09:13:55 -07:00
gsxdsm
e445b3e367 FN-8201: pin pi dependency versions
Pin the pi runtime packages to a single exact version so global npm installs resolve a compatible set.

- Pin pi-ai and pi-coding-agent declarations across workspace manifests
- Add a guard and tests that reject ranged or mismatched pi versions
- Document the source-install fallback and add a patch changeset

Files changed:
 .changeset/fn-8201-pin-pi-versions.md              |   7 ++
 docs/getting-started.md                            |   3 +
 package.json                                       |   6 +-
 packages/cli/package.json                          |   4 +-
 packages/cli/src/__tests__/package-config.test.ts  |  18 +++-
 packages/core/package.json                         |   2 +-
 packages/dashboard/package.json                    |   2 +-
 packages/engine/package.json                       |   4 +-
 packages/pi-claude-cli/package.json                |   8 +-
 .../__tests__/check-pi-versions-pinned.test.mjs    |  45 ++++++++
 scripts/check-pi-versions-pinned.mjs               | 120 +++++++++++++++++++++
 11 files changed, 205 insertions(+), 14 deletions(-)

Fusion-Task-Id: FN-8201

Fusion-Task-Lineage: bf0ac363-df5f-4445-835b-cfd2d4909659

Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
2026-07-17 09:12:01 -07:00
gsxdsm
6ca7e48f87 fix(FN-7965): surface duplicate decisions
Show a clear operator-decision badge and deliver an idempotent mailbox prompt for triage duplicate markers.

Fusion-Task-Id: FN-7965
2026-07-17 08:53:44 -07:00
gsxdsm
c6adac6e7c fix(dashboard): stop mobile task-detail panel shifting left
The full-screen mobile task-detail sheet hides all resize handles, so
FN-8015's `margin-inline-end: var(--space-lg)` gutter on the shared
`.floating-window__body` (added to keep the scrollbar clear of desktop
resize hot zones) only added dead space on the right and shifted the
entire panel left. Zero it for `.floating-window--task-detail` inside
the mobile breakpoint so `.detail-body`'s own padding defines both
insets equally; desktop resize-handle clearance is untouched.

Refined the FN-8015 invariant test to enforce the desktop hot-zone gutter
media-aware (strips @media blocks) and added a regression guard for the
mobile zeroing.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-17 08:35:44 -07:00
gsxdsm
f27965d711 FN-8200: add Settings theme selector spacing
Add tokenized spacing below the Settings current-theme selector before Font Size.

- Scope the spacing to the Settings current-theme row without affecting compact dropdowns.
- Add a regression test for the established spacing token.
- Add a patch changeset for the Settings layout fix.

Files changed:
 .changeset/fn-8200-theme-selector-spacing.md                      | 6 ++++++
 packages/dashboard/app/components/ThemeDropdown.css               | 8 ++++++++
 .../dashboard/app/components/__tests__/ThemeDropdown.test.tsx     | 6 ++++++
 3 files changed, 20 insertions(+)

Fusion-Task-Id: FN-8200

Fusion-Task-Lineage: 6b72d0f7-759f-49aa-a8f9-f788cc9ec5c5

Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
2026-07-17 08:17:09 -07:00
gsxdsm
f866e68c6a FN-8199: sync translated README updates
Bring localized README feature and storage descriptions in line with the English documentation.

- Add PostgreSQL storage and documentation references across translations.
- Refresh model, task chat, theme, GitHub, dashboard, and model override descriptions.
- Align package storage terminology with the PostgreSQL migration.

Files changed:
 README.es.md    | 16 +++++++++-------
 README.fr.md    | 16 +++++++++-------
 README.ko.md    | 16 +++++++++-------
 README.zh-CN.md | 16 +++++++++-------
 README.zh-TW.md | 16 +++++++++-------
 5 files changed, 45 insertions(+), 35 deletions(-)

Fusion-Task-Id: FN-8199

Fusion-Task-Lineage: ba617549-e43e-4531-bdfe-4e79f94ffdae

Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
2026-07-17 01:29:30 -07:00
gsxdsm
7b540959c5 FN-8198: add Todo item task creation API
Add API endpoints that turn Todo lists and items into executable board tasks.

- Expose Todo list and item read endpoints with async store parity.
- Create tasks from Todo items with validation, project scoping, provenance, and workflow-default placement.
- Document the scripting flow and cover dashboard and PostgreSQL behavior.

Files changed:
 .changeset/fn-8198-todo-api.md                     |   7 +
 docs/todo-view.md                                  |  18 ++-
 .../src/__tests__/postgres/todo-store.pg.test.ts   |  13 ++
 packages/core/src/async-todo-store.ts              |  17 +++
 .../dashboard/src/__tests__/todo-routes.test.ts    | 157 ++++++++++++++++++++-
 packages/dashboard/src/todo-routes.ts              | 128 ++++++++++++++++-
 6 files changed, 335 insertions(+), 5 deletions(-)

Fusion-Task-Id: FN-8198

Fusion-Task-Lineage: f8d5032b-4844-43c9-97d1-3cc7d13d0ce1

Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
2026-07-17 01:23:32 -07:00
gsxdsm
a51cba0d69 FN-8197: move merge details to summary tab
Keep completed task merge metadata with its completion summary.

- Relocate the Merge Details card from Definition to the done-only Summary tab.
- Cover merge-detail states and Definition/mobile containment with dashboard tests.
- Document the Summary tab behavior and add a patch changeset.

Files changed:
 .changeset/fn-8197-merge-details-summary-tab.md    |  7 +++
 docs/dashboard-guide.md                            |  2 +-
 packages/dashboard/app/components/TaskDetailModal.tsx   |  4 +-
 packages/dashboard/app/components/TaskSummaryTab.tsx    |  9 +++
 packages/dashboard/app/components/__tests__/TaskDetailModal.responsive-and-dependencies.test.tsx | 49 ++++++++++------
 packages/dashboard/app/components/__tests__/TaskDetailModal.summary-tab.test.tsx | 65 ++++++++++++++++++++++
 6 files changed, 115 insertions(+), 21 deletions(-)

Fusion-Task-Id: FN-8197

Fusion-Task-Lineage: f4aac014-1e8d-4b8f-91f1-ba40ae879508

Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
2026-07-17 01:17:22 -07:00
gsxdsm
6183621ca3 FN-8196: add mobile footer stat tooltips
Make compact mobile executor footer statistics identifiable on tap.

- Add accessible tap targets and portaled tooltips for mobile footer statistics.
- Dismiss stat tooltips on repeat tap, outside interaction, Escape, scrolling, and viewport changes.
- Cover mobile behavior while preserving desktop and tablet inline labels.
- Document the interaction and add a patch changeset.

Files changed:
 .changeset/fn-8196-mobile-footer-stat-tooltips.md  |   7 +
 docs/dashboard-guide.md                            |   2 +
 .../dashboard/app/components/ExecutorStatusBar.css |  47 +++++-
 .../dashboard/app/components/ExecutorStatusBar.tsx | 166 +++++++++++++++++++--
 .../__tests__/ExecutorStatusBar.test.tsx           | 132 +++++++++++++++-
 5 files changed, 333 insertions(+), 21 deletions(-)

Fusion-Task-Id: FN-8196

Fusion-Task-Lineage: c566b614-739d-4538-85c5-a8ff62720f4d

Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
2026-07-17 01:12:08 -07:00
gsxdsm
7f175b036b FN-8195: label replan task status
Display the needs-replan task state with a clear localized operator-facing label.

- Map needs-replan status badges to Replan centrally.
- Add English i18n resources and generated resource typings.
- Cover the status-label mapping and add a patch changeset.

Files changed:
 .changeset/fn-8195-replan-badge.md                                | 7 +++++++
 .../dashboard/app/utils/__tests__/taskStatusBadgeLabel.test.ts    | 4 ++++
 packages/dashboard/app/utils/taskStatusBadgeLabel.ts              | 8 ++++++++
 packages/i18n/locales/en/app.json                                 | 2 ++
 packages/i18n/src/resources.d.ts                                  | 2 ++
 5 files changed, 23 insertions(+)

Fusion-Task-Id: FN-8195

Fusion-Task-Lineage: 7ad115b8-6405-426e-9201-bfa1258fff5e

Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
2026-07-17 01:07:52 -07:00
gsxdsm
79b05c62ea FN-8190: apply heartbeat multipliers to agent health
Align dashboard health labels with project-resolved heartbeat cadence.

- Load the project heartbeat multiplier in agent list and detail views.
- Apply the effective cadence once when determining stale agents.
- Cover long-cadence and multiplier boundary health states.

Files changed:
 .../dashboard/app/components/AgentDetailView.tsx   | 19 ++++++++++++--
 .../dashboard/app/components/AgentListModal.tsx    | 19 ++++++++++++--
 packages/dashboard/app/components/AgentsView.tsx   |  2 +-
 .../__tests__/AgentDetailView.test-helpers.ts      |  3 +++
 .../components/__tests__/AgentListModal.test.tsx   | 27 ++++++++++++++++++++
 .../app/utils/__tests__/agentHealth.test.tsx       | 29 ++++++++++++++++++++++
 packages/dashboard/app/utils/agentHealth.tsx       | 25 ++++++++++++++++---
 7 files changed, 115 insertions(+), 9 deletions(-)

Fusion-Task-Id: FN-8190

Fusion-Task-Lineage: 1efcdc19-503a-4893-b277-f65e900a399b

Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
2026-07-17 01:01:53 -07:00
gsxdsm
87ffb24fca FN-8194: align task detail inline controls
Align task-detail actions with Quick Add while preserving existing integrations.

- Add inline attachment and eligible GitHub tracking controls
- Replace the Oversight menu dots with an Eye icon and reorder metadata actions
- Cover action order, integration behavior, icon rendering, and mobile wrapping

Files changed:
 .changeset/fn-8194-task-detail-inline-controls.md  |  7 ++
 docs/dashboard-guide.md                            |  6 +-
 .../dashboard/app/components/TaskDetailModal.tsx   | 99 +++++++++++++++-------
 ...lModal.inline-editing-and-integrations.test.tsx | 80 ++++++++++++++++-
 .../TaskDetailModal.mock-coverage.test.ts          |  1 +
 .../TaskDetailModal.oversight-controls.test.tsx    | 19 +++++
 ...etailModal.responsive-and-dependencies.test.tsx |  5 +-
 .../__tests__/TaskDetailModal.test-helpers.ts      | 10 +--
 8 files changed, 187 insertions(+), 40 deletions(-)

Fusion-Task-Id: FN-8194

Fusion-Task-Lineage: 8de0e36f-b428-401c-98aa-35964c556a39

Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
2026-07-17 00:55:57 -07:00
gsxdsm
3fdc2c1fb7 FN-8193: add collapsible sticky provider lists
Keep model-provider headers visible and let users collapse provider model rows with persisted local preferences.

- Add sticky provider headers and accessible collapse toggles to CustomModelDropdown
- Persist collapsed provider groups locally while keeping filtered matches visible
- Cover collapse, keyboard navigation, storage recovery, and empty-state behavior
- Document the dashboard behavior and add a minor changeset

Files changed:
 .changeset/fn-8193-collapsible-provider-lists.md   |   7 ++
 docs/dashboard-guide.md                            |   1 +
 .../app/components/CustomModelDropdown.css         |  48 +++++++-
 .../app/components/CustomModelDropdown.tsx         | 110 ++++++++++++++----
 .../__tests__/CustomModelDropdown.test.tsx         | 129 +++++++++++++++++++++
 5 files changed, 266 insertions(+), 29 deletions(-)

Fusion-Task-Id: FN-8193

Fusion-Task-Lineage: 4fbc43a8-ef81-463c-b4d0-5f00a57ab27b

Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
2026-07-17 00:48:36 -07:00
gsxdsm
c3e98d193f FN-8188: inherit default workflow steps for refinements
Make refinement tasks inherit configured default workflow steps and persist their selection.

- Seed refinement tasks with enabled default workflow steps
- Persist inherited workflow selections after task creation
- Cover populated, empty, and absent default workflows in PostgreSQL tests
- Add a patch changeset for the refinement workflow fix

Files changed:
 .changeset/fn-8188-refinement-workflow-steps.md    |  7 +++
 packages/core/src/__tests__/postgres/refine-duplicate-task.pg.test.ts | 68 ++++++++++++++++++++++
 packages/core/src/task-store/update-task-deps.ts   | 42 ++++++++++++-
 3 files changed, 116 insertions(+), 1 deletion(-)

Fusion-Task-Id: FN-8188

Fusion-Task-Lineage: aed1e63a-f828-426c-9d3d-3a4ba17443c1

Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
2026-07-17 00:42:00 -07:00
gsxdsm
b779785286 FN-8187: update README feature overview
Refresh the README to describe current Fusion capabilities and storage guidance.

- Highlight embedded PostgreSQL, Kimi K3, and richer task-chat progress reporting
- Document updated GitHub import, dashboard, and per-task override capabilities
- Add storage documentation to the resource directory

Files changed:
 README.md | 19 +++++++++++++------
 1 file changed, 13 insertions(+), 6 deletions(-)

Fusion-Task-Id: FN-8187

Fusion-Task-Lineage: 696c1f1f-91fe-4256-b6b8-98a6d6b2f2e5

Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
2026-07-17 00:36:21 -07:00
gsxdsm
ac52438554 FN-8192: prevent mobile confirmation ghost-click dismissal
Keep mobile task-delete confirmations open through delayed compatibility clicks.

- Gate backdrop dismissal until the opening gesture settles.
- Cover deliberate backdrop dismissal and mobile ghost-click behavior.
- Document the regression and add a patch changeset.

Files changed:
 .changeset/fn-8192-mobile-confirm-ghost-click.md   |  7 +++
 .../confirm-dialog-mobile-ghost-click-dismiss.md   | 53 ++++++++++++++++++++++
 .../dashboard/app/components/ConfirmDialog.tsx     | 23 +++++++++-
 .../components/__tests__/ConfirmDialog.test.tsx    | 34 ++++++++++++--
 .../app/hooks/__tests__/useConfirm.test.ts         | 32 ++++++++++++-
 5 files changed, 143 insertions(+), 6 deletions(-)

Fusion-Task-Id: FN-8192

Fusion-Task-Lineage: ea08a6c0-0f2e-4ec3-8ad7-23cdc4a5d7f1

Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
2026-07-17 00:29:48 -07:00
gsxdsm
39887f5c87 FN-8186: refine mobile Quick Add action sizing
Refine mobile Quick Add controls so every action glyph is readable without compromising compact layout.

- Size option and primary action glyphs consistently with spacing tokens.
- Tighten mobile control padding while preserving touch-target and desktop behavior.
- Extend CSS contract coverage and add a patch changeset.

Files changed:
 .changeset/fn-8186-quick-add-mobile-icon-sizing.md |  7 +++++
 .../quick-entry-action-row-height-parity.test.tsx  | 35 ++++++++++++++--------
 .../dashboard/app/components/QuickEntryBox.css     | 31 +++++++++++--------
 3 files changed, 48 insertions(+), 25 deletions(-)

Fusion-Task-Id: FN-8186

Fusion-Task-Lineage: 9433521c-6c06-4f89-a486-b8073f2a2961

Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
2026-07-17 00:23:19 -07:00
gsxdsm
f0c32dc1bf FN-8191: clamp conversation action menus to viewport
Keep conversation overflow menus aligned with their triggers and contained in the visible viewport.

- Measure rendered menu dimensions so theme-specific widths remain aligned
- Clamp horizontal and vertical menu positions for trigger and context-menu entry points
- Cover desktop alignment and mobile alternate-theme viewport containment

Files changed:
 packages/dashboard/app/components/ChatView.css     |  6 ++
 packages/dashboard/app/components/ChatView.tsx     | 72 +++++++++++++++++++++-
 .../__tests__/ChatView.core-interactions.test.tsx  | 32 ++++++++++
 .../components/__tests__/ChatView.mobile.test.tsx  | 41 +++++++++++-
 4 files changed, 147 insertions(+), 4 deletions(-)

Fusion-Task-Id: FN-8191

Fusion-Task-Lineage: 1a52140b-9450-421d-844b-9fff302d8ea4

Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
2026-07-17 00:17:59 -07:00
gsxdsm
0d2dcb3a40 FN-8189: fix mobile task-detail scrollbar gutter
Hide the mobile task-detail scrollbar to keep content insets visually symmetric.

- Preserve touch scrolling while removing the mobile non-overlay scrollbar track
- Add responsive regression coverage for scrollbar and inset behavior
- Add a patch changeset for the mobile layout fix

Files changed:
 .../fn-8189-task-detail-mobile-scrollbar-gutter.md |  7 +++
 .../dashboard/app/components/TaskDetailModal.css   | 12 +++++
 ...etailModal.responsive-and-dependencies.test.tsx | 55 ++++++++++++++++++++++
 3 files changed, 74 insertions(+)

Fusion-Task-Id: FN-8189

Fusion-Task-Lineage: 3a9300f7-9732-417b-95b0-b65a79efaaf6

Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
2026-07-17 00:04:41 -07:00
gsxdsm
6206af8ce9 FN-8146: restore persistent theme selection
Restore the full Settings theme picker while preserving Shadcn Mono selections across reloads.

- Merge the Settings selector into the current-theme row with shared dropdown controls.
- Restore Shadcn Mono across theme metadata, token styles, and dashboard/desktop bootstrap validation.
- Scope Mono light swatches correctly and cover restored options and persistence with tests.

Files changed:
 .changeset/FN-8146-theme-dropdown-current-row.md   |  7 +++
 docs/dashboard-guide.md                            |  5 ++-
 packages/core/src/types/execution-and-ui.ts        |  1 +
 .../dashboard/app/components/ThemeDropdown.css     | 52 ++++++++++++++++++++++
 .../dashboard/app/components/ThemeDropdown.tsx     | 42 ++++++++++++++---
 .../dashboard/app/components/ThemeSelector.css     | 42 +----------------
 .../dashboard/app/components/ThemeSelector.tsx     | 45 +++++--------------
 .../components/__tests__/ThemeDropdown.test.tsx    | 46 ++++++++++++++++---
 .../components/__tests__/ThemeSelector.test.tsx    | 29 ++++++++----
 .../__tests__/CommandCenterControls.test.tsx       | 15 ++++++-
 packages/dashboard/app/components/themeOptions.ts  |  2 +
 .../dashboard/app/hooks/__tests__/useTheme.test.ts |  8 ++--
 packages/dashboard/app/hooks/useTheme.ts           |  8 ++--
 packages/dashboard/app/index.html                  |  5 +--
 packages/dashboard/app/public/theme-data.css       | 16 ++++++-
 packages/desktop/src/renderer/index.html           |  1 +
 16 files changed, 213 insertions(+), 111 deletions(-)

Fusion-Task-Id: FN-8146

Fusion-Task-Lineage: 32ec7f21-ee6b-4459-8e7b-f5a5435c4994

Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
2026-07-17 00:00:46 -07:00
TrinaryCompute
bf6a974b58 fix(postgres): scope the cross-process merge guard to the project (#2267)
## fix(postgres): scope the cross-process merge guard to the project

The guard's own comment (`project-engine.ts`) says it checks whether
another process is merging a task **"for this project"** — and in SQLite
mode the per-project DB file made that scoping implicit.
`getActiveMergingTaskImpl`'s `backendMode` branch queries the shared PG
`tasks` table with **no `project_id` filter**, so one merging task
anywhere serializes merges across **all** projects.

### Production evidence
6-project embedded-PG deployment: **697 cross-project `Merge deferred …
is already merging (cross-process guard)` retries in 10 minutes** — six
independent repos waiting on each other's serialized merger, collapsing
merge throughput ~6x and letting `in-review` pile up to 95 tasks.

### Fix
Add the existing `taskProjectScope(layer)` filter to the query's
conditions (one line + import). It is a no-op when the layer carries no
`projectId`, so single-project deployments and the SQLite path are
unchanged. Same pattern as the other project-scoped task queries.

Deployed on the affected instance: cross-project merges now proceed in
parallel; per-project serialization (the guard's documented intent) is
preserved.

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

## Summary by CodeRabbit

* **Bug Fixes**
* Improved merge task handling so activity in one project no longer
unnecessarily blocks merge operations in other projects.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->

Co-authored-by: TrinaryCompute <fusion-merge@trinarycompute.dev>
2026-07-16 23:46:41 -07:00
gsxdsm
7760d783bd fix: green full-suite after getAgentLogCount and inventory drift (#2266)
## Summary
- Follow-up after #2229: full suite on main still failed on dashboard
curated inventory (21 ungated files) and mass engine failures
(`this.store.getAgentLogCount is not a function`).
- Harden executor tool-failure cursor capture for minimal/test
`TaskStore` adapters (same optional-API pattern as `project-engine`),
keep mock fixtures in lockstep, and quarantine inventory-only dashboard
files with ledger + vitest exclude.

## Changes
- **Executor**: optional `getAgentLogCount` / `getAgentLogs` /
`updateTask` at graph entry and trailing-failure detection.
- **Mocks**: `createMockStore`, soft-delete guard, post-done
continuation, cron `getGlobalSettingsDir`, executor-prompt
`bulkCompletionRefusalAt` (FN-8141).
- **i18n** (prior commit): es/fr/ko/zh-CN/zh-TW triage-duplicate keys.
- **Inventory**: 21 dashboard files → `test-quarantine.json` +
`vitest.config.ts` lockstep (VAL-REMOVAL SQLite / load flakes /
build-only dist assert).

## Test plan
- [x] `node scripts/check-test-inventory.mjs --dashboard-curated`
- [x] `pnpm test:gate`
- [x] engine: soft-delete, prompt, cron, post-done, tool-failure-retry,
and related samples
- [x] `@fusion/core` schema-applier + `@fusion/i18n` parity
- [ ] Full Suite (non-blocking) on this PR / main after merge

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

## Summary by CodeRabbit

- **New Features**
- Added localized text for triage duplicate-resolution settings and
near-duplicate task actions in Spanish, French, Korean, Simplified
Chinese, and Traditional Chinese.
- Users can now see translated options and confirmations to keep or
delete detected duplicate tasks.

- **Bug Fixes**
- Improved resilience during task execution and recovery when optional
activity-log services are unavailable, preventing avoidable failures
during error handling.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2026-07-16 23:44:35 -07:00
gsxdsm
13bdf63bfc fix: resolve Anthropic subscription auth through getApiKey for pi ModelRuntime.getAuth
pi >=0.80.8 moved session request auth to ModelRuntime.getAuth -> pi-ai
resolveProviderAuth, which reads credentials.read("anthropic") and refreshes an
OAuth credential via credentials.modify("anthropic"). Fusion stores the
subscription login under `anthropic-subscription` with no raw `anthropic` row,
so the refresh callback saw current===undefined, bailed, and auth resolved to
undefined -> "Provider is not configured: anthropic" (then fell back).

Resolve read("anthropic") through fusion's getApiKey (refresh + raw/legacy/
subscription/fallback precedence) and hand pi-ai a ready api_key credential;
pi-ai routes it as OAuth by the sk-ant-oat token prefix. Supersedes the prior
read-alias, which fixed lookup but not the broken OAuth refresh-via-modify path.

Verified end-to-end: ModelRuntime.getAuth(anthropic/opus) now resolves the
subscription token instead of returning undefined.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-16 23:24:45 -07:00
gsxdsm
4b150e2280 fix: resolve Anthropic subscription auth under pi-ai >=0.80 read() contract
pi-ai >=0.80 resolves provider auth via credentials.read(provider.id) and
performs OAuth refresh/derivation itself, bypassing fusion's getApiKey()
where the anthropic-subscription -> anthropic alias lived. A subscription-only
login surfaced as "Provider is not configured: anthropic" at prompt time even
though the status card showed connected.

- Alias the subscription OAuth credential into read("anthropic") at the
  credential-store layer (createFusionCredentialStore); raw/legacy rows still win.
- Match "not configured" in isRetryableModelSelectionError so an unresolved
  provider triggers the configured fallback model instead of hard-failing.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-16 23:09:12 -07:00
TrinaryCompute
00cca460fc fix(engine): reclaim leaked semaphore slots when the system is busy, not only at total idle (#2265)
## fix(engine): reclaim leaked semaphore slots when the system is busy,
not only at total idle

### The bug

`recoverIdleSemaphoreLeakCandidate` only reclaims stale `AgentSemaphore`
slots when the system is **completely** idle (`persistedActive === 0 &&
inFlightCount === 0` → reconcile to 0). If even one in-progress row
persists — e.g. a zombie task whose agent session died without its
`finally` release — the valve never opens, and slots leaked by abnormal
teardown accumulate monotonically until `activeCount` pins the limit.

At that point the engine deadlocks in a distinctive way:
- every hold/release sweep logs `Hold release for <task> deferred — no
reservable slot for in-progress`
- triage/plan report `planning=0 … processing=0, semaphore
active=<limit>/<limit>, available=0`
- the merge queue grows unboundedly (merges also need a slot)
- only a process restart recovers

`reapLeakedConcurrencySlots` (FN-6782) doesn't help — it reconciles
**worktree** slots, not the shared semaphore.

### Production evidence

Observed twice on a 6-project embedded-PG deployment driving a local
model:

- After ~5 days of continuous operation: `semaphore active=24/24`,
`planning=0/24, processing=0`, 5 persisted in-progress rows (dead
sessions), merge queue at 88, **zero merges for >24h**. Restart
immediately restored merging.
- Same signature earlier at `active=40/40` with both LLM backends idle
(`kvcache≈0`).

The handful of zombie in-progress rows kept `persistedActive` nonzero
indefinitely, so the idle-only valve could never fire.

### The fix

Generalize the valve: clamp `activeCount` down to the **persisted +
in-flight bound** whenever the semaphore over-holds **continuously** for
a repair window.

- The strict-idle case (`bound === 0`) keeps its existing fast 5s window
— behavior unchanged, existing tests pass as-is.
- The non-idle case uses a deliberately conservative new window
(`STALE_SEMAPHORE_EXCESS_REPAIR_MS = 600_000`, 10 min): nested helper
agents (`runNested`) legitimately push `activeCount` above the persisted
top-level count for the duration of a nested run, so the excess must
outlive any plausible nested session before it is treated as leaked. The
candidate timestamp resets the moment the excess clears.
- `reconcileActiveCount` only ever lowers the count, so the clamp cannot
inflate capacity; a late release from a genuinely live agent after a
(worst-case, mis-timed) clamp is absorbed by the existing excess-release
guard (FN-6423).

Call-site changes are limited to the two log messages (the old
parenthetical claimed "no persisted … agent work", which is no longer
the only repair case).

### Tests

- existing idle-valve tests pass unchanged (same window, same
reconcile-to-0)
- new: stale excess above a nonzero persisted bound is repaired only
after the long window, and clamps exactly to the bound
- new: candidate resets when the excess clears (nested overshoot ending)
- new: caller in-flight sessions count into the bound (no false
candidate)

### Files

- `packages/engine/src/concurrency.ts` — generalized valve +
`STALE_SEMAPHORE_EXCESS_REPAIR_MS`
- `packages/engine/src/scheduler.ts`, `packages/engine/src/triage.ts` —
log message accuracy
- `packages/engine/src/__tests__/concurrency.test.ts` — 3 new tests


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

* **Bug Fixes**
* Improved stale “semaphore excess” recovery by using a configurable
repair window when excess persists.
* Prevented premature capacity corrections by accounting for in-flight
top-level work during reconciliation.
* Correctly handles nested helper activity so only leaked excess is
reclaimed, preserving legitimate nested runs.
* Updated reconciliation to clamp excess to the appropriate reclaim
floor instead of waiting indefinitely.
* **Improvements**
* Refreshed diagnostic warning text to clarify the over-held vs
persisted+in-flight work comparison.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->

---------

Co-authored-by: TrinaryCompute <fusion-merge@trinarycompute.dev>
Co-authored-by: gsxdsm <gsxdsm@users.noreply.github.com>
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-16 23:08:42 -07:00
gsxdsm
d1e9b563f7 fix(FN-8141): add forward migration for tasks.bulk_completion_refusal_at
PR #2260 added project.tasks.bulk_completion_refusal_at to the Drizzle model
and the 0000 baseline but shipped no forward migration. Databases created
before #2260 already carry the 0000 marker, so the applier skips the baseline
and they never gained the column — every such cluster crashed on the first
TaskStore SELECT ("column bulk_completion_refusal_at does not exist"), taking
down dashboard/app boot.

Adds forward migration 0018 (wired via BULK_COMPLETION_REFUSAL_AT_VERSION;
SCHEMA_BASELINE_VERSION -> "0018") so existing clusters heal on next startup.

Prevention:
- Per-column upgrade regression test reproducing the exact existing-DB failure.
- Migration-wiring-integrity guard (no PostgreSQL): SCHEMA_BASELINE_VERSION must
  equal the highest migration file, and every .sql must be registered in the
  applier so none silently never runs.
- Repairs 6 pre-existing schema-applier tests left stale by the 0017 addition
  (baseline-marker identity + version-list enumerations).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-16 22:47:33 -07:00
gsxdsm
da7c68c1dc refactor: package code organization wave 8 (#2252)
## Summary

Wave 8 of package code organization (plan:
`docs/plans/2026-07-14-001-refactor-package-code-organization-plan.md`),
after #2166.

### Peels
| New module | Parent |
|---|---|
| `app/api/board-workflows.ts` | `legacy.ts` |
| `app/api/remote.ts` | `legacy.ts` |
| `app/api/memory.ts` | `legacy.ts` |
| `types/docker-nodes.ts` | `types.ts` |
| `task-store/task-store-helpers.ts` | `remaining-ops-10.ts` (rename) |

### CI fix (main unblocker)
FN-8142 requires pi SDK **0.80.8+** (`ModelRuntime`,
`CredentialStore.list`). Floored `@earendil-works/pi-*` to `^0.80.8` so
lockfile resolves past 0.80.6.

Public import paths stay stable via re-exports.

### LOC
- `legacy.ts` ~10975 → ~10561
- `types.ts` ~7349 → ~6928

## Test plan
- [x] core / dashboard typecheck + eslint on peels
- [x] api-tasks (66)
- [x] CI merge gate (Lint / Typecheck / Build / Gate)

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

## Summary by CodeRabbit

* **New Features**
* Added dashboard support for multi-lane board workflows, including
workflow metadata retrieval and task custom-field updates.
* Added dashboard memory client capabilities for reading/writing memory,
managing memory files, compaction/dreaming, insights, auditing, backend
status, and retrieval testing.
* Added remote connectivity controls, including tunnel start/stop,
provider activation, token generation, URL sharing, and QR generation.
* Added Docker node configuration validation plus secure redaction for
sensitive fields in responses.
* **Improvements**
* Consolidated dashboard API exports into dedicated modules and
preserved the legacy API surface via re-exports.
* **Chores**
  * Updated related agent dependency versions.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2026-07-16 22:36:51 -07:00
gsxdsm
daa34fbc38 fix: refineTask/duplicateTask fail in backend (PostgreSQL) mode (#2253)
## Summary

Eliminates the remaining backend/PostgreSQL-mode sync-SQLite
(`store.db`) call sites — both the crashing ones and the
try/catch-masked ones that silently degraded features. Found via a full
audit of `store.db`/`archiveDb` residue after the PG cutover's per-site
routing missed them.

**Crashes fixed:**
1. **refineTask / duplicateTask** threw `TaskStore.db: SQLite Database
is not available in backend mode`. Both create rows through
`createTaskWithId` callbacks calling `store.atomicCreateTaskJson()`
directly, bypassing `_createTaskInternal`'s backend routing. The shared
helper now routes itself (soft-delete conflict check + non-destructive
insert in one AsyncDataLayer transaction).
2. **Merger verification cache**: `getVerificationCacheHit` ran sync
SQLite unguarded *outside* any try/catch in
`runDeterministicVerification`; `recordVerificationCachePass` was
swallowed so the cache never warmed. Both are now async with a PG
branch.

**Silent degradations fixed (features that were dead on PG):**
- Workflow run-branch + foreach step-instance persistence
(`saveWorkflowRunBranch`, `loadWorkflowRunBranches`,
`clearWorkflowRunBranches`, `saveWorkflowRunStepInstance`,
`loadWorkflowRunStepInstances`, `clearWorkflowRunStepInstances`) —
executor crash-resume checkpoints were silently never persisted.
- `getBranchProgressByTask` — returned an empty map, dropping
`branchProgress` from task payloads.
- `runPluginColumnTransitionHooks` — plugin `onEnter`/`onExit`
column-transition hooks never fired (marker bookkeeping + non-locking
task read now async).
- `getTaskColumns` — dashboard treated all agent-linked tasks as
non-terminal.
- `getWorkflowStep` / `listWorkflowSteps` — stored workflow-step rows
now read from `project.workflow_steps` (listing previously returned
plugin steps only); `getLegacyWorkflowStepSnapshot` returns `undefined`
on PG (legacy snapshot exists only in pre-migration SQLite).
- `readRawProjectSettings` / `listWorkflowPromptOverridesForProject` —
now read via the async layer.

These store methods became **async**; engine/dashboard callers await
them (the workflow persistence interfaces already accepted
`Promise`-returning impls).

**PG gotcha encoded in the fixes:** migration `0006_project_ownership`
rebuilds every project-schema PK to lead with `project_id`, so
column-list `ON CONFLICT` inference fails (42P10) — upserts target the
PK by constraint name.

## Surface Enumeration

- Creators through `atomicCreateTaskJson`: `refineTaskImpl`,
`duplicateTaskImpl` (fixed); `_createTaskInternalImpl` unaffected
(already routed).
- Verification-cache callers (all merger, all 3 sites now awaited).
- Run-branch/step-instance callers: executor persistence adapters,
parse-steps foreach probe, integration-queue flip, crash-resume
reconcile, graph-reset cleanup; triage replan cleanup; dashboard
spec-rebuild pin clears; agent-reflection rework summing — all awaited.
- Audit classified everything else as guarded or sync-mode-only (dead in
production — every entry point constructs stores via
`createTaskStoreForBackend`).

## Symptom Verification

- **Original symptoms:** refinement/duplicate creation threw; merge
verification threw; workflow checkpoints/branch progress/plugin
hooks/task-column lookups silently no-oped on PostgreSQL.
- **Exact reproduction:** `refine-duplicate-task.pg.test.ts`,
`verification-cache.pg.test.ts`, and
`sync-db-residue-backend.pg.test.ts` exercise each surface against
embedded-PostgreSQL backend-mode TaskStores.
- **Assertion it is gone:** all suites pass (14 + 5 tests), plus
`transition-pending-and-status-clear.pg.test.ts`,
`create-task-reserved-id.pg.test.ts`, dashboard `routes-github.test.ts`
(123), engine `triage.test.ts` (221) and `agent-reflection.test.ts`
(31). Core/engine/dashboard typecheck fully clean: the 13 errors from
the FN-8142 pi SDK migration are fixed by bumping
@earendil-works/pi-ai/pi-coding-agent to ^0.80.10 (FN-8142 used APIs
absent from the previously locked 0.80.6). Locally green: `pnpm
verify:fast` (scoped typecheck + build + CLI build + boot smoke), `pnpm
test:gate`, and `pnpm lint`.

🤖 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**
* Fixed refinement/duplication task creation in PostgreSQL-backed
backend mode.
* Improved backend-mode persistence for workflow checkpoints,
foreach-step instances, branch progress, and cleanup flows (including
retries/resets/transitions), so stored data reliably round-trips.
* Hardened backend-mode reads for workflow steps, task columns, project
settings, and prompt overrides.
* Made verification-cache reads/writes complete reliably, including
command-specific cache behavior.
* **Tests**
* Added PostgreSQL integration/regression coverage for
refinement/duplication, sync residue, and verification caching.
* **Chores**
* Bumped `@earendil-works/pi-ai` and `@earendil-works/pi-coding-agent`
to `^0.80.10`.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->

---------

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
2026-07-16 22:35:01 -07:00
gsxdsm
c64c1014e2 test(FN-8146): speed up PG test harness with per-process schema template
Applying the full schema baseline on every fresh test database cost ~530ms
per file and dominated PG gate wall-time under fork contention. Apply the
schema once per worker process into a pid-keyed template database, then
create each test DB via CREATE DATABASE ... TEMPLATE (fast server-side copy).

Dead-pid templates from crashed/prior runs are swept before creating a new
one; template copies are serialized since CREATE DATABASE ... TEMPLATE forbids
concurrent access to the source. Removes the throwaway probe2 timing test.

Fusion-Task-Id: FN-8146

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-16 22:22:21 -07:00
gsxdsm
5f70447a62 FN-8108: require resolution for triage duplicate tasks
Require explicit resolution for triage-detected duplicate tasks.

- Add configurable prompt, keep, and delete handling for explicit duplicate markers.
- Surface linked duplicate decisions in task details and scheduling settings.
- Preserve task failure provenance and strengthen lifecycle recovery coverage.

Files changed:
 .changeset/blocked-park-survives-graph-teardown.md |   7 +
 .changeset/failure-provenance-promoter-marker.md   |   7 +
 .changeset/fn-8108-triage-duplicate-resolution.md  |   7 +
 .changeset/veto-progressing-does-not-clear.md      |   7 +
 docs/settings-reference.md                         |   2 +
 .../completed-promotion-failure-provenance.test.ts |  31 +++
 .../core/src/__tests__/duplicate-intake.test.ts    |  11 ++
 .../src/completed-promotion-failure-provenance.ts  |  38 +++-
 packages/core/src/duplicate-intake.ts              |  29 +++
 packages/core/src/index.gate.ts                    |   1 +
 packages/core/src/index.ts                         |   3 +-
 packages/core/src/settings-schema.ts               |   1 +
 packages/core/src/types.ts                         |   6 +
 .../dashboard/app/components/TaskDetailModal.tsx   |  38 +++-
 .../__tests__/TaskDetailModal.rendering.test.tsx   |  26 +++
 .../settings/sections/SchedulingSection.search.ts  |   9 +
 .../settings/sections/SchedulingSection.tsx        |  15 ++
 .../__tests__/routes-tasks-near-duplicate.test.ts  |  31 +++
 .../src/routes/register-task-workflow-routes.ts    |  19 ++
 .../__tests__/executor-task-done-blocked.test.ts   | 212 ++++++++++++++++++++-
 packages/engine/src/__tests__/merger-ai.test.ts    |  56 +++++-
 .../__tests__/overseer-noop-finalize-veto.test.ts  |  64 ++++++-
 .../explicit-duplicate-marker-sweep.test.ts        |  19 +-
 packages/engine/src/__tests__/self-healing.test.ts |  53 ++++++
 .../triage-explicit-duplicate-marker.test.ts       |  22 ++-
 packages/engine/src/executor.ts                    |  30 +++
 packages/engine/src/merger-ai.ts                   |   5 +-
 packages/engine/src/overseer-noop-finalize-veto.ts | 148 ++++++++++----
 packages/engine/src/self-healing.ts                |  29 ++-
 packages/engine/src/triage.ts                      |  58 +++---
 packages/i18n/locales/en/app.json                  |  15 +-
 packages/i18n/src/resources.d.ts                   |  13 +-
 32 files changed, 897 insertions(+), 115 deletions(-)

Fusion-Task-Id: FN-8108

Fusion-Task-Lineage: 8e732bad-d418-426e-85e1-903a7f990fba

Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
2026-07-16 22:11:35 -07:00
gsxdsm
0753476c0a fix(engine): no-op finalize veto no longer cleared by mid-execution progressing — only accepted-completion evidence supersedes a failure park (#2263)
## What

FN-8141 follow-up 3. Tightens `deriveExecutorSignalMemory`
(packages/engine/src/overseer-noop-finalize-veto.ts) so a mid-execution
`progressing` overseer observation can no longer clear the executor
no-op-finalize veto.

## Why

The prior derivation took the **newest** executor `observe` entry and
cleared `incompleteWork` whenever it was anything but the canonical
failed reason. But the planner overseer emits a `progressing`
observation ("Task is actively executing in-progress work") the
**moment** a task re-enters execution — long before that execution
finishes. The defeating shape:

> task parks failed-incomplete → requeued → re-execution starts
(overseer observes `progressing`) → execution dies or reverts again
**without** a newer failed observation → newest observation is
`progressing` → `incompleteWork:false` → an empty no-op finalize is
**not** vetoed → the reverted branch launders into `done`.

`progressing` is not "completed green" — the veto's own contract says
the failure must be superseded by a green completion.

## Change

The executor stage in `planner-overseer.ts` emits only
`progressing`/`failed`/`stuck`/`blocked` — **no** green-completion
observation — so the timeline alone cannot distinguish progressing from
completed. Per the follow-up spec, the derivation now:

1. Scans the executor `observe` timeline for the newest
**failed-with-incomplete-work** observation.
2. Keeps `incompleteWork` TRUE unless a durable **clean-completion
task-log marker** is **strictly newer** than that failure park. Reuses
the shared `CLEAN_COMPLETION_MARKERS` set (now exported from
`@fusion/core`, single-sourced with
`evaluateCompletedPromotionFailureProvenance`) so it automatically
tracks sibling follow-up F2's removal of the promotion-output marker.
3. Fails safe on a malformed failure timestamp (stays vetoed).
4. `merger-ai.ts` threads `task.log` into the derivation.

All existing precedence rules are preserved: non-empty merges are never
vetoed; human-control deferral (user-paused / autoMerge:false) still
defers; a missing task fails open.

## Test evidence

- `pnpm --filter @fusion/engine exec vitest run
src/__tests__/overseer-noop-finalize-veto.test.ts
src/__tests__/merger-ai.test.ts` → **59 passed**. New/updated cases:
failed→progressing (no completion) ⇒ still vetoed (the regression this
fixes); progressing between two failed parks (FN-8141 timeline) ⇒
vetoed; clean-completion marker newer than failure ⇒ not vetoed; older
completion marker ⇒ still vetoed; no failure park ⇒ not vetoed; bounded
tail-scan preserved. Integration: empty lane with
failed-then-progressing timeline blocks the finalize; genuinely
re-executed green task finalizes to done.
- `pnpm --filter @fusion/core exec vitest run
src/__tests__/completed-promotion-failure-provenance.test.ts` → 9
passed.
- `pnpm --filter @fusion/engine exec tsc --noEmit` → clean. `pnpm
--filter @fusion/core exec tsc --noEmit` → clean.
- `pnpm verify:fast` → PASS (3 steps green).

🤖 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 empty-merge finalization safeguards so an in-progress task
cannot incorrectly clear a previously detected incomplete-work failure.
* Finalization can now proceed when a newer clean-completion record
confirms successful completion.
* Added bounded task-history evaluation to ensure completion records are
interpreted safely and consistently.

* **Tests**
* Expanded coverage for progressing, failed, and clean-completion task
timelines.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->

Co-authored-by: Claude Opus <noreply@anthropic.com>
2026-07-16 21:42:44 -07:00
gsxdsm
46866a5c5a fix(core): promoter recovery output no longer counts as clean-completion evidence in the failure-provenance guard (#2262)
## What

FN-8141 follow-up 2. Removes `"Auto-recovered: task work was complete
but stranded"` from `CLEAN_COMPLETION_MARKERS` in
`packages/core/src/completed-promotion-failure-provenance.ts`.
Clean-completion evidence is now **execution outcomes only**: `"Task
marked done by agent"` (accepted explicit fn_task_done, also covers the
PREMISE STALE skip-then-done flow) and `"All steps complete — implicit
fn_task_done"` (implicit-completion success).

## Why

That string is the PROMOTER'S OWN OUTPUT — self-healing's
`recoverCompletedTasks` (executor.ts:4594) narrating "I promoted this
task" — not evidence of a clean execution outcome. Any task whose
durable log contains a promotion written by the pre-#2257 buggy sweep
(the real FN-8141 row, or any pre-guard history) carried a permanent
"clean" marker: the tail scan hit the promotion line before the older
failure park and returned not-blocked, re-enabling the exact laundering
the guard exists to stop.

Audit confirmed no other genuine execution-outcome success markers are
missing — the PREMISE STALE accepted `fn_task_done` writes the
already-listed `"Task marked done by agent"` line (executor.ts:14939),
and the honest-blocked exit (`BLOCKED: ...`) is correctly NOT counted.
`grep` confirmed the removed string has only one other consumer: its
writer at executor.ts:4594. A task already promoted to in-review/done is
out of the promoters' todo/in-progress scan scope, so
legitimately-recovered old tasks are not wedged (verified by test rather
than assumed).

## Test evidence

- Core `completed-promotion-failure-provenance.test.ts`: **11 passed** —
added pre-fix-history shape (failure park → promoter recovery line →
blocked), promoter-line-alone → blocked, and positive coverage of each
remaining marker.
- Engine `self-healing.test.ts`: **405 passed** — added promoter
withholds on the pre-fix-history shape and emits the existing
`task:reconcile-stranded-completed-no-action` (reason
`failure-provenance`) event.
- `pnpm --filter @fusion/engine exec tsc --noEmit`: clean.
- `pnpm verify:fast`: PASS (3 steps green, no tests run).

🤖 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**
- Prevented failed tasks with prior failure history from being
automatically promoted.
- Ensured recovery messages cannot override authoritative failure
records or be mistaken for successful completion.
- Preserved the existing no-action behavior and audit event when
promotion is blocked by failure provenance.
- **Tests**
- Added regression coverage for failed-task promotion and stranded-task
recovery scenarios.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->

Co-authored-by: Claude Opus <noreply@anthropic.com>
2026-07-16 21:39:49 -07:00
gsxdsm
f116d05c41 fix(engine): honest BLOCKED park survives pause-abort and workflow-graph teardown (#2264)
## What

Follow-up 1 to the FN-8141 guard series (#2254–#2260). Makes the honest
`fn_task_done(outcome="blocked")` park (`status:"failed"`,
`error:"BLOCKED: <reason>"`, blockedBy → dependencies, added in #2256)
**survive the graph-teardown machinery** that bounced FN-8141's failed
park back to `todo`.

## Why

In the original FN-8141 incident, the executor's parked-failed state did
not stick: the pause-abort classifier and the workflow-graph failure
handler either rehomed the task to `todo` (clearing `status`/`error`) or
overwrote the distinctive `BLOCKED:` error with a generic "Workflow
graph terminated with failure" string. #2256 added the blocked exit but
nobody proved the park survives that bounce. Any path that
clears/overwrites the marker re-opens the laundering hole, because
self-healing (#2257/#2260) and dependency-gated scheduling key off
exactly that `BLOCKED:` error plus the recorded `blockedBy`
dependencies.

`handleGraphFailure` now detects a live blocked park (`status ===
"failed" && error.startsWith("BLOCKED:")`) **before every other
classifier** and honors it, following the existing non-graph honor-park
precedent (executor `~12163`):

- no requeue to `todo`, no engine-internal auto-continue, no `BLOCKED:`
error overwrite;
- clears the in-memory pause-abort marker so
`recoverPausedAbortFailures` has nothing to chase;
- **releases the worktree / `maxWorktrees` slot** (FN-6782 leaked-holder
precedent — the graph `finally` does not delete `activeWorktrees`);
- leaves `status`/`error`/`column`/`dependencies`/steps untouched.

Unblocking still works: the operator requeue (`moveTask`
in-progress→todo, `moves.ts ~628`) and `buildManualRetryResetPatch`
clear the `BLOCKED:` error; the guard keys off the **live** error, so a
cleared row is never re-wedged, and dependency-gated scheduling leaves
the parked row untouched while `blockedBy` deps are unmet.

## Surfaces covered

Pause-abort classifier (hard-cancel), engine-internal auto-continue, and
the plain terminal graph-failure sink — all routed through
`handleGraphFailure`, so a single top-of-method guard composes across
them.

## Test evidence

Extended `executor-task-done-blocked.test.ts` (drives
`handleGraphFailure` against a live blocked park):
- honors the park under a hard-cancel pause-abort bounce (no requeue /
clear / auto-continue);
- honors it under a plain terminal graph failure (sink never overwrites
`BLOCKED:`);
- releases the worktree/concurrency slot + clears the pause-abort
marker;
- NON-blocked failed park keeps existing behavior (guard scoped to
`BLOCKED:`);
- a cleared (unblocked) row is NOT re-honor-parked.

```
pnpm --filter @fusion/engine exec vitest run src/__tests__/executor-task-done-blocked.test.ts  → 13 passed
pnpm --filter @fusion/engine exec vitest run executor-paused-abort-todo-benign + executor-graph-requeue-gate  → 53 passed
pnpm --filter @fusion/engine exec tsc --noEmit  → clean
pnpm verify:fast  → PASS (3 steps green)
```

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

Co-authored-by: Claude Opus <noreply@anthropic.com>
2026-07-16 21:39:16 -07:00
gsxdsm
bc7dfe4bbf FN-8178: fix task-card context menu autofocus dismissal
Prevent portaled task-card menus from closing when autofocus creates a scroll event.

- Focus the first menu action with preventScroll while preserving keyboard access.
- Cover all card menu entry points and intentional dismissal paths.
- Document the interaction lifecycle fix and publish a patch changeset.

Files changed:
 .changeset/fn-8178-context-menu-flash-dismiss.md   |  7 +++
 docs/dashboard-guide.md                            |  2 +-
 .../task-card-context-menu-flash-dismiss-layers.md | 35 +++++++++++
 .../dashboard/app/components/TaskContextMenu.tsx   |  9 ++-
 .../app/components/__tests__/TaskCard.test.tsx     | 69 ++++++++++++++++++++++
 5 files changed, 120 insertions(+), 2 deletions(-)

Fusion-Task-Id: FN-8178

Fusion-Task-Lineage: 2eb94ffd-e922-497d-8904-1aea8bf760b1

Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
2026-07-16 21:13:30 -07:00
gsxdsm
e72629c251 FN-8126: add per-task merger model controls
Enable Quick Add and task editing to select merger models and thinking levels.

- Persist merger model and thinking overrides through task APIs, storage, and PostgreSQL migrations.
- Add merger-lane selection controls to Quick Add and model settings interfaces.
- Apply task merger settings to merger and PR fallback sessions, with regression coverage.
- Document the merger lane and include a release changeset.

Files changed:
 .changeset/fn-8126-quick-add-merger-lane.md        |   7 ++
 docs/dashboard-guide.md                            |   2 +
 docs/settings-reference.md                         |   7 +-
 .../core/src/__tests__/model-resolution.test.ts    |   9 ++
 packages/core/src/index.gate.ts                    |   1 +
 packages/core/src/index.ts                         |   1 +
 packages/core/src/model-resolution.ts              |  20 ++++
 .../core/src/postgres/migrations/0000_initial.sql  |   3 +
 .../migrations/0017_task_merger_model_lane.sql     |   4 +
 packages/core/src/postgres/schema-applier.ts       |  14 ++-
 packages/core/src/postgres/schema/project.ts       |   3 +
 packages/core/src/store.ts                         |   2 +-
 .../core/src/task-store/archive-lifecycle-2.ts     |   6 ++
 packages/core/src/task-store/persistence.ts        |   6 ++
 packages/core/src/task-store/remaining-ops-2.ts    |   4 +-
 packages/core/src/task-store/remaining-ops-6.ts    |   2 +-
 packages/core/src/task-store/serialization.ts      |   6 ++
 packages/core/src/task-store/task-creation.ts      |   6 ++
 packages/core/src/task-store/task-row-mappers.ts   |   4 +-
 packages/core/src/task-store/task-update.ts        |   6 ++
 packages/core/src/types.ts                         |  18 ++++
 packages/dashboard/app/api/tasks.ts                |  13 +++
 .../dashboard/app/components/InlineCreateCard.tsx  |  33 ++++++-
 .../app/components/ModelSelectionModal.tsx         |  29 ++++++
 .../dashboard/app/components/ModelSelectorTab.tsx  | 101 +++++++++++++++++++--
 .../dashboard/app/components/QuickEntryBox.tsx     |  44 +++++++--
 .../__tests__/ModelSelectionModal.test.tsx         |  20 ++++
 .../components/__tests__/ModelSelectorTab.test.tsx |  37 +++++++-
 .../src/routes/register-task-workflow-routes.ts    |  27 +++++-
 .../src/__tests__/agent-session-helpers.test.ts    |   8 ++
 packages/engine/src/agent-session-helpers.ts       |  16 +++-
 packages/engine/src/merger-ai.ts                   |  14 +--
 packages/engine/src/merger.ts                      |  35 ++++---
 packages/engine/src/pr-response-run-ops.ts         |   7 +-
 34 files changed, 451 insertions(+), 64 deletions(-)

Fusion-Task-Id: FN-8126

Fusion-Task-Lineage: 3fc81801-6d77-4e11-9cf0-3af37313930e

Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
2026-07-16 21:07:06 -07:00
gsxdsm
b747f51f8b FN-8185: use async persistence for reflection rework reads
Resolve workflow rework metrics from the persisted production run.

- Resolve workflow selections to definition-backed run IDs
- Prefer asynchronous step-instance reads with legacy-store compatibility
- Cover PostgreSQL, fallback, and degraded reflection metrics

Files changed:
 .../engine/src/__tests__/agent-reflection.test.ts  | 51 +++++++++++++++++++---
 packages/engine/src/agent-reflection.ts            | 50 ++++++++++++++-------
 2 files changed, 80 insertions(+), 21 deletions(-)

Fusion-Task-Id: FN-8185

Fusion-Task-Lineage: 2554d5f1-62b7-47a6-bad0-c00a7d649a2d

Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
2026-07-16 20:50:18 -07:00
gsxdsm
30aaab209c FN-8170: suppress stale planning status badges
Hide stale planning badges from Todo and In Progress task surfaces.

- Centralize the planning badge suppression predicate.
- Apply it to board cards and desktop/mobile list views.
- Cover suppressed and preserved status badge behavior.

Files changed:
 packages/dashboard/app/components/ListView.tsx     | 11 +++--
 packages/dashboard/app/components/TaskCard.tsx     | 16 +++++--
 .../app/components/__tests__/ListView.test.tsx     | 53 ++++++++++++++++++++++
 .../app/components/__tests__/TaskCard.test.tsx     | 40 ++++++++++++++++
 .../utils/__tests__/taskStatusBadgeLabel.test.ts   | 21 ++++++++-
 .../dashboard/app/utils/taskStatusBadgeLabel.ts    | 14 ++++++
 6 files changed, 147 insertions(+), 8 deletions(-)

Fusion-Task-Id: FN-8170

Fusion-Task-Lineage: e2a00154-8537-4eb3-aadb-45b42f13f15b

Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
2026-07-16 20:45:40 -07:00
gsxdsm
a136535f15 fix(engine): taint steps skipped after a bulk-completion refusal so they cannot auto-promote (#2260)
## What & why

**FN-8141 laundered a failed task into `done` with zero net changes and
no sign-off.** After the executor's
`bulk-step-completion-without-review` refusal fired (steps had no
APPROVE verdicts), the agent used the sanctioned skip affordance
(`fn_task_update status="skipped"`) on the remaining unreviewed steps.
Because every completion check counts `skipped` as complete, the task
then satisfied the exact condition the refusal was protecting, and
downstream **automatic** promotion (implicit `fn_task_done`,
self-healing `recoverStrandedCompletedTodoTasks`) moved it to in-review
— where the AI merger found an empty diff and finalized it as a no-op
`done`.

This PR restores the invariant: **steps skipped while a
bulk-step-completion refusal marker is active on the task are "tainted"
and cannot carry the task to review through any automatic path.** The
taint clears on an honest exit — an accepted `fn_task_done` (explicit or
non-tainted implicit) or an operator manual retry — so the legitimate
`PREMISE STALE` skip-then-done flow is unaffected.

## Design

- **Persisted marker**: new nullable `Task.bulkCompletionRefusalAt` (ISO
timestamp), stamped when the `bulk-step-completion-without-review`
refusal fires (explicit `fn_task_done` handler + implicit
`handleImplicitTaskDoneRefusal`). Survives requeue so a refusal on
attempt N taints attempt N+1's promotion. Full store plumbing (types,
descriptors, serialization, SQLite/PG schema + health self-heal).
- **Pure evaluator** `evaluateSkipBypassTaint(task)` in `@fusion/core`
(next to `evaluateNoCommitsNoOpFinalize`): `blocked` iff the marker is
set AND ≥1 step is `skipped`. Single rule every AUTO-promotion check
calls.
- **Clearing**: accepted explicit `fn_task_done`, accepted
implicit/retry completion (the success-reset `updateTask`s), and
`buildManualRetryResetPatch` (operator retry). A fresh lifecycle that
genuinely re-does the work leaves zero skipped steps, so it is never
blocked even if a marker lingers.

## Surface enumeration (every consumer of "all steps done/skipped" that
gates AUTO-promotion)

- **executor.ts**: `getCompletedTaskFinalizationDecision` (gated on the
`isTaskWorkComplete` branch only, never on an accepted `taskDone`);
`recoverCompletedTask` (shared chokepoint for unpause resume,
completed-task watchdog, orphan resume);
`evaluateImplicitCompletionRefusal` (both implicit-completion loops);
`isTaskAlreadyCompleteForNonContinuableSession`; graph merge-boundary
`getWorkflowMergeImplementationProofFailure`.
- **self-healing.ts**: `recoverCompletedTasks` (stuck in-progress) and
`recoverStrandedCompletedTodoTasks` (the exact FN-8141 promoter).
- **Verified-safe, left as-is**: per-step graph node projections
(executor ~6274/6298) and progress-render checks — they don't gate
whole-task auto-promotion.

## Test evidence

Scoped runs (all green):

```
CORE:   pnpm --filter @fusion/core exec vitest run \
          src/__tests__/skip-bypass-taint-guard.test.ts \
          src/__tests__/skip-bypass-taint-persistence.test.ts \
          src/__tests__/manual-retry-reset.test.ts
        → 17 passed

ENGINE: pnpm --filter @fusion/engine exec vitest run \
          src/__tests__/executor-skip-bypass-taint.test.ts \
          src/__tests__/self-healing.test.ts
        → 401 passed
```

Coverage: pure-evaluator (skip-before-refusal counts, skip-after-refusal
doesn't, taint-clearing, empty-marker/empty-steps edges); store
round-trip of the marker (set→read→clear); executor white-box (implicit
completion refused when tainted, allowed when clean or fully re-done,
graph merge-boundary reports missing proof, and the **explicit
`fn_task_done` PREMISE-STALE honest exit stays accepted**); self-healing
(FN-8141 sequence does not promote from either recovery path; a clean
legitimately-skipped task still promotes); manual-retry clears the
marker.

## Note on `pnpm verify:fast`

`verify:fast` currently fails at the workspace-artifact bootstrap on
**pre-existing** pi-SDK type errors in
`packages/engine/src/{auth-storage,pi,provider-registration}.ts` — the
FN-8145 upstream migration breakage (pi 0.80.x removed
`AuthStorage`/`ModelRegistry.create`). **None of those files are in this
diff.** `@fusion/core` builds clean (`packages/core build: Done`), and
`@fusion/engine` `tsc` reports **no errors in the files this PR
touches** (`executor.ts`, `self-healing.ts`); the only engine build
errors are the FN-8145 files. This base failure is the same condition
FN-8141 describes and is out of scope for this task.

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

Co-authored-by: Claude Opus <noreply@anthropic.com>
2026-07-16 20:37:05 -07:00
gsxdsm
29543a0aac FN-8157: add PostgreSQL workflow step-instance persistence
Persist workflow foreach step-instance state through async PostgreSQL store APIs.

- Add async save, load, and stale-run pruning operations backed by Drizzle.
- Route executor persistence, recovery, and integration projection through async APIs.
- Cover PostgreSQL persistence and migrate foreach wiring coverage to the PG harness.
- Quarantine unrelated flaky route and triage tests per the test ledger.

Files changed:
 .../workflow-run-step-instances.pg.test.ts         | 100 +++++++++++++++++++
 packages/core/src/store.ts                         |  14 ++-
 packages/core/src/task-store/remaining-ops-6.ts    | 109 ++++++++++++++++++++-
 .../dashboard/src/__tests__/routes-github.test.ts  |  14 +--
 .../src/routes/register-task-workflow-routes.ts    |  18 ++--
 packages/engine/src/__tests__/triage.test.ts       |   6 +-
 .../src/__tests__/workflow-foreach-wiring.test.ts  |  59 +++++------
 packages/engine/src/executor.ts                    |  57 ++++++++---
 packages/engine/src/triage.ts                      |   4 +-
 packages/engine/vitest.config.ts                   |   2 +-
 scripts/lib/test-quarantine.json                   |   7 +-
 11 files changed, 315 insertions(+), 75 deletions(-)

Fusion-Task-Id: FN-8157

Fusion-Task-Lineage: c359f0d3-9191-4d27-aaed-9912419c5c27

Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
2026-07-16 20:36:45 -07:00
gsxdsm
aa1e250dd3 fix(engine): overseer vetoes no-op merge finalize when the last executor signal was failed-with-incomplete-work (#2258)
## What & why

FN-8141 (\"Update pi SDK … verify Kimi K3 end to end\") was **laundered
into `done`** despite producing zero net changes. The executor reverted
the impossible work 5 times; the planner overseer emitted
`stage=executor signal=failed` (\"Executor stage parked failed with work
incomplete\") **twice**, then an hour later — because the overseer is
**stage-scoped and memoryless** — classified the same task `stage=merger
signal=progressing` and let the AI merger's **empty no-op finalize**
promote it to `done`. No reviewer ever saw it (skipped steps request no
review; the merge-review pass reviews an empty diff).

This restores the invariant: **a task whose most-recent executor signal
is failed-with-incomplete-work, with no subsequent green completion,
must not reach `done` via a zero-diff no-op merge finalize.**

## Change

Two pure, unit-testable, never-throw functions
(`packages/engine/src/overseer-noop-finalize-veto.ts`), following the
FN-7514 `evaluateOverseerHumanControl` precedent (pure predicate +
ids/outcomes-only audit metadata):

- **`deriveExecutorSignalMemory`** — reconstructs the most-recent
executor signal from the **durable `overseer:intervention` timeline the
overseer already writes** (no new persisted column / migration; \"the
existing oversight state storage the controller uses\"). A later green
executor observation supersedes an earlier failure, which is how \"no
subsequent execution completed green\" is derived. Keys on the
newly-exported `EXECUTOR_FAILED_INCOMPLETE_REASON` constant (already
load-bearing for FN-7577 feed dedup) as the single source of truth.
- **`evaluateNoOpFinalizeExecutorVeto`** — the veto decision.

Wired into the AI **empty-merge lane** (`merger-ai.ts`), composed with
(and independent of) the FN-6461 no-commits guard: on veto it sets
`error`, writes a durable log entry, emits
`overseer:no-op-finalize-vetoed-failed-executor`, and moves the task
back to `todo` with progress preserved — mirroring the FN-6461 blocked
lane. The move-to-todo transition takes the task out of the merge lane,
so the event isn't re-emitted every poll (equivalent to the
`overseer:oversight-withheld-human-control` per-(taskId, reason) dedup).

Independent of the sibling Task 2 merger-layer lineage guard: both can
fire; **either alone stops FN-8141**.

## Surface enumeration

- **Empty (zero-diff) no-op merge** — vetoed when memory is
failed-incomplete. ✅
- **Non-empty (real squash landed) merge** — **never** vetoed
(reviewers/merge review cover real diffs). ✅
- **failed-incomplete then later green execution** — memory superseded →
no veto. ✅
- **No executor memory / store lacks the async reader** — fail open (no
veto); FN-6461 + sibling guard remain the safety net. ✅
- **user-paused / autoMerge:false / approval-blocked** — defers to
FN-7514 human-control; never fights those semantics. ✅
- **Timeline entry filtering** — only `stage=executor` +
`action=observe` entries count as signals (retry/escalate messages
ignored). ✅

## Test evidence

`pnpm --filter @fusion/engine exec vitest run
src/__tests__/overseer-noop-finalize-veto.test.ts
src/__tests__/merger-ai.test.ts --silent=passed-only --reporter=dot`

```
Test Files  2 passed (2)
     Tests  48 passed (48)
```

Covers: derivation (failed→veto, failed-then-green→no-veto,
non-executor/non-observe ignored, empty→null); evaluator (veto, green,
no-memory, non-empty never-vetoed, user-paused defer, autoMerge:false
defer, missing-task fail-open); and an engine integration test driving
an **FN-8141-shaped** empty merge through `runAiMerge` → asserts
move-to-todo + `overseer:no-op-finalize-vetoed-failed-executor` audit
event + main untouched, plus the later-green case finalizing done.

`@fusion/core` builds clean (`pnpm --filter @fusion/core build`).

**Note on `pnpm verify:fast`:** it currently fails to build
`@fusion/engine`, but **only** in `auth-storage.ts` / `pi.ts` /
`provider-registration.ts` — the pre-existing pi-SDK breakage that *is*
this incident (pi 0.80.x removed
`AuthStorage`/`ModelRuntime`/`ModelRegistry`; tracked as FN-8145).
Verified identical errors with my changes stashed; **my diff touches
none of those files and adds zero new type errors** (tsc reports all
program errors before failing — none were in my files).

🤖 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**
- Prevented zero-change tasks from being incorrectly finalized when the
latest executor attempt failed with unfinished work.
- Preserved task progress and returned affected tasks to **Todo** for
continued processing.
- Allowed finalization to proceed after a subsequent successful executor
result.
  - Maintained existing human-control and non-empty merge behavior.
  - Added audit visibility for blocked finalization events.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->

Co-authored-by: Claude Opus <noreply@anthropic.com>
2026-07-16 20:29:46 -07:00
gsxdsm
50179ed5eb FN-8167: suppress failed UI during automatic recovery
Prevent transient automatic-retry tasks from appearing as terminal failures.

- Centralize pending-recovery and manual-retry presentation rules.
- Suppress failed styling, failure alerts, and Retry actions across list, card, and detail views.
- Cover recovery timing and desktop/mobile task surfaces with regression tests.

Files changed:
 .changeset/fn-8167-transient-retry-affordance.md   |  7 ++
 packages/dashboard/app/components/ListView.tsx     | 16 ++---
 packages/dashboard/app/components/TaskCard.tsx     | 13 ++--
 .../dashboard/app/components/TaskDetailModal.tsx   | 13 ++--
 .../app/components/__tests__/ListView.test.tsx     | 38 +++++++++++
 .../app/components/__tests__/TaskCard.test.tsx     | 22 +++++++
 .../__tests__/TaskDetailModal.rendering.test.tsx   | 26 ++++++++
 .../app/utils/__tests__/taskRecovery.test.ts       | 77 ++++++++++++++++++++++
 packages/dashboard/app/utils/taskRecovery.ts       | 32 +++++++++
 9 files changed, 215 insertions(+), 29 deletions(-)

Fusion-Task-Id: FN-8167

Fusion-Task-Lineage: 60d599e2-2e0e-46e7-ad16-cc6a836b5ac7

Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
2026-07-16 20:28:05 -07:00
gsxdsm
f079245acc fix(engine): defensive getAuth guard on session routing seam + realign pi tests to ModelRuntime seam (#2261)
## What / Why

**Reworked after FN-8179 (`fd43a57a4`) landed on `main`.** FN-8179 did
most of what the original PR #2261 did — pinned `@earendil-works/pi-ai`
/ `pi-coding-agent` to `^0.80.10` everywhere, added the
`createSessionOptions` `NonNullable` typing in `pi.ts`, and regenerated
`pnpm-lock.yaml`. This PR was rebased onto current `origin/main` and
reduced to **only the unique residual not covered by FN-8179**.

### Residual change 1 — defensive `getAuth` guard

The FN-8142 migration rewrote `attachSessionRoutingHeaders` from the
`ModelRegistry.getApiKeyAndHeaders` seam to `ModelRuntime.getAuth`, but
dropped the pre-migration defensive invariant: a missing resolution
method must **not** break session creation. On `main` the function now
calls `modelRuntime.getAuth.bind(...)` unguarded, which throws if
`getAuth` is ever absent.

This restores the guard: no-op (warn) when `getAuth` is missing, so a
future pi rename degrades to un-tagged requests instead of a hard
failure at every agent start.

### Residual change 2 — test realignment (needed against current main)

FN-8179 aligned the SDK but did **not** update the two `#1675`
routing-header test suites, which still asserted the old
`getApiKeyAndHeaders` seam. **Verified RED on current `main` before
touching them:**

- `pi-create-fn-agent.test.ts` — **60 / 104 failing** (mock had no
`ModelRuntime` export; `createAgentSession` now receives
`modelRuntime`).
- `pi-session-routing-headers.test.ts` — **4 / 5 failing**
(`attachSessionRoutingHeaders` signature is `getAuth`, not
`getApiKeyAndHeaders`).

Both are realigned to the `ModelRuntime.getAuth` seam (the mock gains
the `ModelRuntime` export) → **109 / 109 green**. Assertions were
strengthened to the new behavior, not weakened; the #1675 precedence
invariant (taskId > pi session id > no-wrap), header merge,
apiKey/provider-header passthrough, failed/undefined passthrough, and
absent-method no-op are all still asserted.

## Surface enumeration

- **Routing-header seam**: both the `createFnAgent` path and the
`attachSessionRoutingHeaders` unit (taskId / pi-session-id / no-id
precedence; header merge; apiKey + provider-header passthrough;
failed/undefined resolution; absent-method no-op).
- **Both mock forms** in `pi-create-fn-agent.test.ts`: the top-level
`vi.mock` and all three `vi.doMock` skill-selection blocks now export
`ModelRuntime`.

## Test evidence

- `pnpm --filter @fusion/engine exec tsc --noEmit` → **exit 0**.
- `pnpm --filter @fusion/engine exec vitest run` on both suites →
**109/109 pass** (was 64 failing on main).
- `pnpm verify:fast` → **PASS** (typecheck + build scoped to changed
packages + CLI build + boot smoke `GET /api/health 200`; no tests run).

Do not merge without CI. No release performed.

🤖 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 session creation reliability when authentication support is
unavailable.
* Preserved existing authentication details while adding session-routing
headers when supported.
* Prevented session creation from failing when authentication
information cannot be resolved.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->

Co-authored-by: Claude Opus <noreply@anthropic.com>
2026-07-16 20:20:59 -07:00
gsxdsm
ced0e84f5d FN-8184: unify heartbeat multiplier timing
Keep scheduler repair and reports health aligned with the effective heartbeat cadence.

- Apply heartbeatMultiplier once through shared interval calculations.
- Align scheduler repair and reports-health stale thresholds with scaled cadence.
- Add multiplier regression coverage and document the effective timing rules.

Files changed:
 .../fn-8184-heartbeat-multiplier-consistency.md    |   7 ++
 docs/agents.md                                     |   8 +-
 .../src/__tests__/heartbeat-executor.test.ts       |  43 +++++++++
 .../src/__tests__/heartbeat-scheduler.test.ts      |  46 ++++++++++
 packages/engine/src/agent-heartbeat.ts             | 101 ++++++++++++++++-----
 5 files changed, 176 insertions(+), 29 deletions(-)

Fusion-Task-Id: FN-8184

Fusion-Task-Lineage: 05b79c5e-444a-42c3-9073-9c8c05df5def

Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
2026-07-16 20:16:36 -07:00
gsxdsm
95dbf9558d FN-8150: expand mobile import issue lists
Allow mobile issue lists to fill the available import-sheet height.

- Make the mobile import pane a constrained flex column with internal scrolling.
- Remove the 50vh list cap while preserving pagination and action access.
- Cover the responsive list sizing rules with a regression test.

Files changed:
 .../dashboard/app/components/GitHubImportModal.css     | 18 +++++++++++++++++-
 .../components/__tests__/GitHubImportModal.test.tsx    | 17 +++++++++++++++++
 2 files changed, 34 insertions(+), 1 deletion(-)

Fusion-Task-Id: FN-8150

Fusion-Task-Lineage: 92841921-7769-4304-b1f3-8c91aa18efb6

Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
2026-07-16 20:11:06 -07:00
gsxdsm
e10ba2826f FN-8149: consolidate task-card move actions in three-dot menu
Consolidate task-card movement controls into the shared three-dot action menu.

- Remove standalone Send back and Move dropdown controls and their styles.
- Preserve all in-review move targets, including Triage and Done (no merge), in the action menu.
- Update task-card desktop and mobile coverage for the consolidated menu.

Files changed:
 packages/dashboard/app/components/TaskCard.css     |  63 +--------
 packages/dashboard/app/components/TaskCard.tsx     | 150 +++------------------
 .../__tests__/TaskCard.badge-wrap.test.tsx         |  39 ++----
 .../app/components/__tests__/TaskCard.test.tsx     | 134 +++++-------------
 .../app/components/__tests__/board-mobile.test.tsx |   6 +-
 5 files changed, 69 insertions(+), 323 deletions(-)

Fusion-Task-Id: FN-8149

Fusion-Task-Lineage: 650e20f6-c660-4c29-aa65-eb156bed7a0e

Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
2026-07-16 20:04:22 -07:00
gsxdsm
aaa95305cb FN-8148: fix Respecify workflow replanning
Make Respecify reliably return tasks to their workflow's planning lane.

- Resolve task-specific planner lanes with recovery rehoming for custom workflows
- Return the persisted needs-replan task for in-place replans and cover legacy triage
- Hide unsupported Respecify actions for archived tasks

Files changed:
 .changeset/fn-8148-respecify-fix.md                |   7 ++
 .../dashboard/app/components/TaskContextMenu.tsx   |  10 +-
 .../components/__tests__/TaskContextMenu.test.tsx  |   6 +-
 .../dashboard/src/__tests__/routes-github.test.ts  | 103 ++++++++++++++++++++-
 .../src/routes/register-task-workflow-routes.ts    |  66 ++++++-------
 5 files changed, 152 insertions(+), 40 deletions(-)

Fusion-Task-Id: FN-8148

Fusion-Task-Lineage: 7184bd58-e841-4bb1-a7fa-c1c336d6270a

Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
2026-07-16 19:57:17 -07:00