Commit Graph

38 Commits

Author SHA1 Message Date
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
4e4b6be1b4 Plan-approval mailbox notification + triage Plan Review convergence (#2237)
## Summary

Two related changes around the plan-approval flow, plus a fix for
triage's plan-review loop that was stranding tasks at the replan cap.

### 1. Post a mailbox message when a plan needs approval (`feat`)
The ntfy push on `awaiting-approval` already existed end-to-end. This
adds the missing **durable, in-dashboard record**: a `system`-typed
mailbox message linking to the task, written whenever a task enters
`awaiting-approval`.
- Fires **before** the notifications-enabled gate, so a dashboard-only
operator (no ntfy/webhook configured) still gets the record — the whole
point of the mailbox channel.
- `system` type avoids re-triggering the `message:agent-to-user` ntfy
pipeline (no double-notify); idempotent via `sendMessageOnce` (key
`plan-approval:<taskId>`).

### 2. Help triage Plan Review converge before the replan cap (`fix`)
Investigation of three tasks that burned all 8 plan-review replans
without converging (**FN-7996, FN-8105, FN-8108**) found the reviewer
surfaced a *new, deeper* issue each cycle instead of confirming its
prior ones were fixed (goalpost movement), and reviewed specs at
implementation altitude. This addresses the root causes:
- **Feed the spec reviewer its own prior REVISE feedback + the 1-based
replan attempt** so it verifies prior issues rather than moving
goalposts. Gated to `reviewType === "spec"` and `attempt > 1` — **code
review and normal plan review are byte-for-byte unaffected**
(double-verified).
- **Reviewer prompt:** converge-on-re-review rule (don't REVISE for your
own earlier miss), severity ratchet (critical-only at attempt ≥ 3), and
a **Spec Altitude** guard so exact SQL/lock/CAS protocol design is
deferred to code review.
- **Planner prompt:** front-load exhaustive surface enumeration before
writing File Scope, and a storage-architecture ground-truth note
(Postgres-only store, composite PK `(project_id, id)`, `schema-applier`
migrations) to stop the repeated stale-fact REVISE rounds.

## Testing
- `@fusion/core` + `@fusion/engine` typecheck clean.
- Added coverage: reviewer spec-convergence wiring (attempt gating +
code/plan exclusion + severity ratchet), triage prior-feedback
derivation (incl. empty-output→notes fallback), mailbox decoupling
(fires when push disabled) + rejection safety, and assertions for all
new prompt sections.
- Affected suites green: notification-service, reviewer,
triage-plan-review-replan-cap, triage-replan-feedback, agent-prompts.
- `pnpm check:changesets` passes (2 changesets: `@runfusion/fusion`
minor + patch).

## Review
Ran a 6-persona `ce-code-review` (correctness + adversarial on Opus;
maintainability, testing, project-standards, api-contract). Guards
verified unbreakable; no P0/P1 correctness or security issues. Applied
the resulting fixes: decoupled the mailbox write from the push gate
(P2), `??`→`||` in the feedback derivation (P3), de-duplicated the
`specConvergence` ternary (P3), and closed the test-coverage gap the
review flagged.

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

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

- **New Features**
- Plans transitioning to **awaiting approval** now write an idempotent,
task-linked **dashboard mailbox** message (approval reason + direct task
link), even if push/notifications are disabled.
- **Bug Fixes**
- Plan Review **replan** behavior now better **converges** on prior
REVISE feedback (including notes fallback) and stops looping at the
replan cap.
- At later attempts (attempt 3+), **REVISE** is applied to **critical**
issues while lower-severity items shift to suggestions.
- **Tests**
- Added/expanded coverage for mailbox messaging and spec-convergence
prompt wiring.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->

---------

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-16 14:44:07 -07:00
gsxdsm
335b6a4dc2 fix: raise Plan Review replan cap to 8 and explain approval holds
Give planner/reviewer pairs more room to converge before escalating, and surface why a task is parked for plan approval—especially plan-review-replan-cap non-convergence—on cards, detail, and notifications.
2026-07-15 11:14:17 -07:00
gsxdsm
278ede9dfa fix(FN-7952): recover provider failures without retry loops
Preserve authenticated CLI usage after migration, surface OAuth remediation, and use a single distinct model fallback before parking permanent failures. Keep transient credential errors retryable and confirm each OAuth expiry notification independently.

Fusion-Task-Id: FN-7952
2026-07-14 15:54:44 -07:00
gsxdsm
c15c78feeb feat: migrate storage from SQLite to PostgreSQL (#1793)
# Migrate storage from SQLite to PostgreSQL — full dashboard cutover

Migrates Fusion's storage layer to the embedded PostgreSQL
`AsyncDataLayer` (the default backend) and **completes the
satellite-store + feature cutover** so every dashboard and Command
Center surface works in PG mode.

## Status — every surface works in embedded-PG mode

Verified live against a running embedded-Postgres dashboard (all
**200**, zero 5xx) and gate-tested (**23 files / 99 tests** on embedded
PG, plus engine-core 294 and ci-shape 63 in the blocking merge gate;
core/engine/cli/dashboard typecheck clean).

| Area | Surfaces | State |
|---|---|---|
| Satellite stores | workflows, todos, insights, research, missions,
goals, mailbox | ✅ |
| Views | artifacts, documents, evals | ✅ |
| Command Center | activity, productivity, team, tokens, tools,
**workflows**, **github**, **signals**, **plugin-activations**, **live**
(all 10) | ✅ |
| Run execution | insight generation, research run execution | ✅
(store-path; AI step needs a provider) |
| Live updates | SSE push for mission/research/insight events | ✅ |
| Workflow editing | create / update / delete / select (+ id counter) |
✅ |
| Engine | mission autopilot, incident-signal ingestion, regression
storm-guard, agent wake-on-message | ✅ |
| Core | tasks, agents, secrets, automations, memory, chat, usage, PRs,
git | ✅ |

## Approach

Each satellite store gets an `Async<Store>` wrapper exposing the sync
store's method names over the existing `async-*-store.ts` helpers;
`get<Store>Store()` returns a `Sync | Async` union; consumers `await`
(harmless on sync), and engine/CLI paths that can't convert use
`instanceof Sync` graceful fallback. Analytics aggregators branch on
`"ping" in dbOrLayer` to run schema-qualified raw SQL over `project.*`
(snake_case) in PG. Executors/orchestrators/autopilot are
await-converted to drive the union store; the async store wrappers
extend `EventEmitter` so SSE live-push fires in both backends.

Not-yet-ported capabilities degrade gracefully (never 500) and are
individually called out in commits.

## Sync with main

The branch is kept continuously merged with `main` (currently through
FN-7845, 2026-07-12); the earlier "final rebase deferred" note no longer
applies. Use **Create a merge commit** (or squash) to land it — GitHub's
rebase-merge cannot replay a merge-maintained branch.

## Residual Review Findings

Multi-agent code review of the PostgreSQL satellite-store ports (U1–U5)
applied 3 safe fixes (see `fix(review): apply autofix feedback`). The
following are **real but gated** — recorded here as follow-up work
rather than auto-applied. All are SQLite→PostgreSQL
**concurrency/atomicity regressions**: the sync stores were immune only
by SQLite's single-writer, single-threaded-handler execution; the async
ports open multi-await read-modify-write windows. **Reachability is low
today** because the execution engines that generate concurrent same-run
mutations (insight run executor, research orchestrator/dispatcher) are
`instanceof`-gated to sync mode in PG. No process-crash class survived
(all engine fallbacks correctly guard the sync store).

- **[P1] Research `appendResearchEvent` dual-write is non-atomic**
(`packages/core/src/async-research-store.ts`, corroborated: adversarial
+ reliability). The `research_run_events` insert (own transaction) and
the `run.events` jsonb update are separate writes — a crash between
them, or two concurrent appends, splits the table count from the jsonb
array. **Fix:** perform the seq-insert and the jsonb update in one
`layer.transactionImmediate`.
- **[P1] Research run terminal-reversion via stale full-row persist**
(`async-research-store.ts` `persistResearchRun`/`updateResearchStatus`).
Concurrent `PATCH /runs/:id/status` + `POST /runs/:id/events` can revert
a terminal run to `running` by overwriting the whole row, bypassing the
transition guard. **Fix:** scoped column `UPDATE`s with a `WHERE status
…` guard, or optimistic version column.
- **[P2] `updateResearchRun`/`updateInsightRun` read-then-write TOCTOU**
— concurrent PATCHes last-writer-wins on the lifecycle merge. **Fix:**
`SELECT … FOR UPDATE` / enclosing transaction.
- **[P2] `upsertRun`/`createRunOrThrowConflict` check-then-create race**
(`async-insight-store.ts`) — two callers can each create an "active"
run. **Fix:** partial unique index on `(projectId, trigger) WHERE status
IN ('pending','running')`.
- **[P3] `createResearchRetryRun` return-value divergence** — sync
returns the pre-update `queued` snapshot; async returns the reloaded
`retry_waiting` run (persisted state is identical). Pick one side for
cross-backend parity.
- **[P2/perf] Mission `getMissionWithHierarchy`/`getMissionHealth` N+1
fan-out** — O(milestones×slices) sequential round-trips hold one pool
slot per request; can starve the pool for large hierarchies. **Fix:**
batched/joined reads.
- **Testing gaps:** no PG-mode concurrency tests (interleaved
status/event mutations), no sync↔async parity assertion for the
lifecycle-error codes, and no mission status/health rollup parity test
vs the sync `MissionStore`.

~~Out of scope (deferred): AI run *execution* (insight/research) +
mission autopilot + live SSE mission events remain sync-gated/degraded
in PG mode.~~ **Since ported** — insight/research run execution, mission
autopilot, and SSE live push all run on the async layer now, which also
makes the concurrency findings above genuinely reachable; they remain
open follow-ups.







---

## Update — 2026-07-12: production-readiness hardening & live acceptance

Everything below landed on this branch since the description above was
written:

**Production blockers from review — fixed**
- `recoverStaleTransitionPending` ported to the async layer (backend
moves write + clear the crash-safe marker; startup/maintenance sweeps no
longer throw).
- Lost-update class fixed: `atomicWriteTaskJson`/`WithAudit` write
changed columns only (full-row upserts silently resurrected stale fields
across concurrent store instances — the "task stuck unplanned forever"
bug).
- First-boot **auto-migration**: booting the PG backend over a project
with a legacy `fusion.db` migrates it automatically (loud failure,
SQLite kept as backup), and the dashboard shows a one-time **"your data
was migrated" banner** with the backup paths and a Need-help Discord
link.
- `pg_dump`/`pg_restore` discovered from common install locations for
embedded-mode backups.
- The PG suite is part of the blocking merge gate (`test:pg-gate`).

**Multi-project isolation (PR #2007, merged into this branch)**
- `project_id` partition key on tasks / archived tasks / config,
`taskProjectScope` threaded through every scan/claim/count, per-project
config rows, layer bound to the project at startup.
- Review P1 follow-up: the shared cold-storage `archive.archived_tasks`
table is also partitioned and all archived-board reads/counts/searches
are scoped.
- Schema drift self-heal generalized to schema-qualified columns so
existing databases upgrade in place.

**Other changes**
- Node settings sync **removed** in PG mode (409
`settings-sync-disabled-postgres`) — nodes share state by connecting to
the same database; auth sync kept (per-machine file).
- Perf (review findings): `listTasks` pushes column filter + ORDER BY +
LIMIT/OFFSET into SQL; `getConversation` capped to the most recent 200
messages.
- Fixed a false "operator action required" pause-abort log fired on
every successfully auto-merged task.

**Live acceptance — PASSED (2026-07-12)**
A sandboxed instance (isolated HOME, embedded PG, real Opus executor)
ran a task through the complete cycle: create → triage (AI spec) →
execute → in-review → AI squash-merge landed on the project's `main` →
done. A write+read sweep of every data surface (settings, comments,
documents, attachments + artifact bridge + artifact edit, chat with real
generation, goals, missions, agent mail, secrets, workflows, memory, CC
analytics) was green on embedded PG.

**Known remaining work**
- The per-project `config` PK re-key has no upgrade path for
pre-isolation embedded-PG databases (needs a real `DROP
CONSTRAINT`/re-key migration; fresh databases are fine).
- `pg_dump`/`pg_restore` binaries are not yet bundled in release
artifacts (PATH/common-location discovery only).
- The satellite-store concurrency findings listed above.

---------

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-authored-by: Phil Larson <hello@phillarson.xyz>
Co-authored-by: fusion-merge <fusion-merge@local>
2026-07-13 19:07:58 -07:00
gsxdsm
0a90dc4b55 FN-7821: refresh-then-recheck OAuth credentials before dispatching expiry notifications
Aligns OAuthExpiryMonitor's ntfy push notifications with the /api/auth/status refresh-then-recheck logic that drives the in-app OAuthReloginBanner, so providers that silently auto-refresh (e.g. GitHub Copilot's ephemeral token) no longer trigger false "OAuth token expired" pushes with no matching banner.

- OAuthExpiryMonitor.check() now performs a best-effort authStorage.getApiKey() refresh and reloads/re-resolves the credential before dispatching oauth-token-expired, instead of relying solely on the stored expiry timestamp
- resolveEffectiveOAuthCredential() now also guards against non-finite expires values in addition to non-numeric ones
- Updated docs/dashboard-guide.md and docs/settings-reference.md to describe the refresh-then-recheck behavior generically (not just Claude/Anthropic) and documented the FN-7821 fix in FNXC provenance comments
- Added regression tests covering the refresh-then-recheck flow in oauth-expiry-monitor.test.ts
- Added a patch changeset describing the fix for release notes

Files changed:
 .changeset/fn-7821-oauth-expiry-notification-banner-consistency.md               |   7 +
 docs/dashboard-guide.md                                                          |   6 +-
 docs/settings-reference.md                                                       |   6 +-
 packages/engine/src/notification/__tests__/oauth-expiry-monitor.test.ts          | 146 ++++++++++++++++++++-
 packages/engine/src/notification/oauth-expiry-monitor.ts                         |  48 ++++++-
 5 files changed, 199 insertions(+), 14 deletions(-)

Fusion-Task-Id: FN-7821

Fusion-Task-Lineage: 5954592c-adda-4fd4-b205-265860eddf3d

Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
2026-07-11 18:07:00 -07:00
gsxdsm
409de31e57 fix: stop false Anthropic OAuth expiry notifications when token is valid
The OAuth expiry monitor and validity logger iterated the un-aliased
getOAuthProviders() id `anthropic` and evaluated get("anthropic"), which
can resolve to a stale legacy/supplemental row (e.g. ~/.pi/agent/auth.json)
even when the fresh, actually-used token lives under `anthropic-subscription`.
That fired a false "Anthropic OAuth expired" notification while the real
subscription token had refreshed successfully.

Both surfaces now resolve the freshest of the two aliased ids via a shared
resolveEffectiveOAuthCredential helper (mirroring the refresh scheduler's
getRefreshCandidateIds alias handling), so a live subscription token
suppresses the false alert. Notification throttle/cadence unchanged.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-08 23:39:35 -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
50f8807037 fix(FN-7233): persist workflow transition notifications
Store workflow transition notification markers as durable task state so recovery alerts do not depend on human-readable log text.
2026-06-29 12:40:00 -07:00
gsxdsm
e09d45037f fix(FN-7233): move workflow lifecycle policy into nodes 2026-06-29 12:23:20 -07:00
gsxdsm
c17d745665 FN-7109: notify operators when CLI agents await input
Notify external providers when terminal-backed CLI agents pause for tool permission or user input.

- Add a dedicated cli-agent-awaiting-input notification event to settings, schema defaults, dashboard options, and provider tests.
- Dispatch CLI-agent waiting-on-input notifications from the in-process runtime with task context and prompt-scoped dedupe keys.
- Document the operator behavior and add a published package changeset for the new notification surface.

Files changed:
 .changeset/fn-7109-cli-agent-notification.md       |   7 ++
 docs/agents.md                                     |   4 +
 docs/settings-reference.md                         |   4 +-
 .../cli-agent-permission-notifications.md          |  13 +++
 .../core/src/__tests__/global-settings.test.ts     |   1 +
 packages/core/src/settings-schema.ts               |   1 +
 packages/core/src/types.ts                         |   6 ++
 .../SettingsModal.remote-notifications.test.tsx    |   1 +
 .../components/__tests__/settings-mobile.test.tsx  |   2 +-
 .../settings/sections/NotificationsSection.tsx     |   3 +
 .../src/__tests__/in-process-runtime.test.ts       |  56 ++++++++++-
 packages/engine/src/__tests__/notifier.test.ts     |   6 +-
 .../engine/src/__tests__/ntfy-provider.test.ts     |   4 +
 .../engine/src/__tests__/webhook-provider.test.ts  |   2 +
 .../engine/src/cli-agent/__tests__/runtime.test.ts |  39 +++++++-
 .../__tests__/notification-service.test.ts         |  56 ++++++++++-
 .../src/notification/notification-service.ts       |   9 +-
 packages/engine/src/notification/ntfy-provider.ts  |   7 ++
 .../engine/src/notification/webhook-provider.ts    |   2 +
 packages/engine/src/notifier.ts                    |   1 +
 packages/engine/src/runtimes/in-process-runtime.ts | 111 +++++++++++++++++++++
 21 files changed, 326 insertions(+), 9 deletions(-)

Fusion-Task-Id: FN-7109

Fusion-Task-Lineage: 636c1875-92a2-46a7-847e-1f4955c0822f

Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
2026-06-27 09:28:31 -07:00
gsxdsm
07a5365115 FN-6093: fix ntfy workflow notification delivery
Ensure workflow and merge-triggered ntfy notifications fire reliably.

- preserve merge-backed task metadata before moving tasks to done so merged notifications retain context
- treat an empty ntfy event allowlist as the documented default event set
- allow failed or no-provider notification attempts to clear dedupe state and retry after settings refresh
- add regression coverage for workflow notify dispatch, stale-settings refresh, and merged-task ntfy delivery

Files changed:
 .changeset/fn-6093-notification-workflow-fix.md    |   5 +
 packages/engine/src/__tests__/merger-ai.test.ts    |   9 +-
 packages/engine/src/__tests__/notification-service.test.ts     | 165 +++++++++++++++++++++
 packages/engine/src/merger-ai.ts                   |  18 ++-
 packages/engine/src/notification/notification-service.ts       |  14 +-
 packages/engine/src/notifier.ts                    |   2 +-
 6 files changed, 208 insertions(+), 5 deletions(-)

Fusion-Task-Id: FN-6093

Fusion-Task-Lineage: fc42cb94-f542-4a34-a325-ffb55dea02f4
2026-06-09 12:50:26 -07:00
gsxdsm
13c6d96fe1 FN-6031: add workflow notification nodes
Add workflow notify nodes with templated notification dispatch support.

- add notify node support to workflow IR, engine handlers, and executor wiring
- expose notify node configuration and summaries in the dashboard editor and node metadata
- add regression tests and a published changeset, plus workflow/settings documentation updates

Files changed:
 .changeset/fn-6031-notification-node.md            |   5 +
 docs/settings-reference.md                         |   6 +-
 docs/workflow-steps.md                             |  10 +-
 packages/core/src/__tests__/workflow-ir.test.ts    |  59 ++++++++++
 packages/core/src/types.ts                         |   4 +-
 packages/core/src/workflow-ir-types.ts             |   4 +-
 packages/core/src/workflow-ir.ts                   |  19 +++
 packages/dashboard/app/components/WorkflowNodeEditor.tsx          |  76 +++++++++++-
 packages/dashboard/app/components/__tests__/WorkflowNodeEditor.test.tsx          |  61 ++++++++++
 packages/dashboard/app/components/__tests__/node-summary.test.ts  |  29 +++++
 packages/dashboard/app/components/__tests__/workflow-flow-mapping.test.ts        |  37 ++++++
 packages/dashboard/app/components/nodes/WorkflowNodeTypes.tsx     |   7 +-
 packages/dashboard/app/components/nodes/node-summary.ts |   5 +
 packages/engine/src/__tests__/workflow-node-handlers-notify.test.ts          | 131 +++++++++++++++++++++
 packages/engine/src/executor.ts                    |   2 +
 packages/engine/src/notification/ntfy-provider.ts  |  11 ++
 packages/engine/src/notification/webhook-provider.ts    |   6 +-
 packages/engine/src/workflow-graph-executor.ts     |   4 +
 packages/engine/src/workflow-graph-task-runner.ts  |   4 +
 packages/engine/src/workflow-node-handlers.ts      |  93 ++++++++++++++-
 20 files changed, 560 insertions(+), 13 deletions(-)

Fusion-Task-Id: FN-6031

Fusion-Task-Lineage: ff5c91e2-3872-4264-9c18-5d9e11628f13
2026-06-09 01:57:16 -07:00
gsxdsm
4ffd0a2dde FN-6030: fix workflow terminal notifications
Ensure merged workflow tasks still emit the expected terminal notifications.

- emit task:merged when PR-driven workflow merges move tasks to done
- send merged notifications for merge-backed done transitions and suppress duplicates
- add regression coverage, a published changeset, and reconcile overlapping docs/test updates

Files changed:
 .../fn-6030-workflow-terminal-notifications.md     |   5 +
 docs/storage.md                                    |   8 +-
 .../core/src/__tests__/builtin-workflows.test.ts   |   1 -
 .../__tests__/store-pr-merged-transition.test.ts   |  14 ++-
 .../src/__tests__/workflow-ir-resolver.test.ts     |   1 -
 packages/core/src/store.ts                         |  14 ++-
 .../settings/sections/ProjectModelsSection.tsx     |   3 +-
 .../engine/src/__tests__/merger-post-merge.test.ts |   9 +-
 .../src/__tests__/notification-service.test.ts     | 129 ++++++++++++++++++++-
 .../__tests__/workflow-graph-task-runner.test.ts   |  57 +++++++++
 .../src/notification/notification-service.ts       |  36 ++++--
 11 files changed, 257 insertions(+), 20 deletions(-)

Fusion-Task-Id: FN-6030

Fusion-Task-Lineage: a991f765-0986-4541-ab38-ff476cf88d16
2026-06-08 13:54:17 -07:00
gsxdsm
a1b7556c7b FN-5924: throttle OAuth expiry alerts across restarts
Persist OAuth expiry alert state so repeated provider expiry warnings are suppressed for 12 hours across engine restarts.

- add a persisted OAuth alert state store under ~/.fusion/agent and share it between the expiry monitor and startup validity logger
- throttle repeated oauth-token-expired notifications and startup expiry warnings per provider for 12 hours, while clearing stale state when providers disappear or change
- cover persisted throttling, restart behavior, failure handling, and wiring updates in engine notification tests
- document the persisted 12-hour OAuth alert throttle and add a patch changeset for @runfusion/fusion

Files changed:
 .changeset/fn-5924-oauth-alert-throttle.md         |   5 +
 docs/settings-reference.md                         |   4 +-
 packages/engine/src/__tests__/project-engine-soft-delete-merge-abort.test.ts |   3 +
 packages/engine/src/__tests__/project-engine.test.ts    |  17 ++-
 packages/engine/src/__tests__/reliability-interactions/soft-delete-in-flight-abort.test.ts            |   3 +
 packages/engine/src/auth-storage.ts                |   6 +-
 packages/engine/src/notification/__tests__/oauth-alert-state.test.ts            |  72 ++++++++++
 packages/engine/src/notification/__tests__/oauth-expiry-monitor.test.ts         | 148 ++++++++++++++++---
 packages/engine/src/notification/__tests__/oauth-validity-logger.test.ts        | 159 ++++++++++++++++++---
 packages/engine/src/notification/index.ts          |   3 +
 packages/engine/src/notification/oauth-alert-state.ts   | 144 +++++++++++++++++++
 packages/engine/src/notification/oauth-expiry-monitor.ts       |  12 +-
 packages/engine/src/notification/oauth-validity-logger.ts      |  16 ++-
 packages/engine/src/project-engine.ts              |  13 +-
 14 files changed, 554 insertions(+), 51 deletions(-)

Fusion-Task-Id: FN-5924
Fusion-Task-Lineage: 83255a25-40c7-44d4-8302-b068ae51250e
2026-06-02 23:13:16 -07:00
gsxdsm
d9e1cdbbd7 FN-5890: include task description in ntfy task-created notifications
Ensure agent-created ntfy notifications fall back to the task description when the title is still empty.

- pass task descriptions through task-created notification payloads
- cover empty-title task creation in notification service tests
- verify ntfy task-created messages render the id+description fallback
- add a patch changeset for the published CLI package

Files changed:
 .changeset/fn-5890-task-created-title.md                 |  5 +++++
 .../engine/src/__tests__/notification-service.test.ts    | 12 +++++++++++-
 packages/engine/src/__tests__/ntfy-provider.test.ts      | 16 ++++++++++++++++
 packages/engine/src/notification/notification-service.ts |  1 +
 4 files changed, 33 insertions(+), 1 deletion(-)

Fusion-Task-Id: FN-5890

Fusion-Task-Lineage: 4a67a4d0-c3a8-4923-a506-7a67a8836127
2026-06-02 10:29:34 -07:00
gsxdsm
5930c18b4d FN-5795: send ntfy notification when agents create tasks
Add agent task-created ntfy notifications with configurable settings and coverage.

- add notification plumbing to emit a dedicated event when an agent creates a task
- extend ntfy provider/settings typing and dashboard settings coverage for the new event toggle
- add regression tests for notification service, notifier integration, and ntfy provider behavior
- add a changeset and documentation updates describing the new notification capability

Files changed:
 .changeset/fn-5795-task-created-notification.md    |  9 +++
 docs/architecture.md                               |  4 +-
 docs/settings-reference.md                         |  4 +-
 docs/storage.md                                    |  2 +-
 packages/core/src/types.ts                         |  4 +-
 .../dashboard/app/components/SettingsModal.tsx     |  1 +
 .../components/__tests__/SettingsModal.test.tsx    | 27 +++++++-
 .../src/__tests__/notification-service.test.ts     | 71 ++++++++++++++++++++++
 packages/engine/src/__tests__/notifier.test.ts     |  5 ++
 .../engine/src/__tests__/ntfy-provider.test.ts     | 32 ++++++++++
 .../src/notification/notification-service.ts       | 53 +++++++++++++++-
 packages/engine/src/notification/ntfy-provider.ts  | 21 +++++--
 12 files changed, 220 insertions(+), 13 deletions(-)

Fusion-Task-Id: FN-5795

Fusion-Task-Lineage: 4cfa3b46-8e9c-451e-ab2d-b7d5c9bf5968
2026-05-31 19:59:32 -07:00
gsxdsm
9f29935525 FN-5778: throttle OAuth expiry notifications per provider
Limit repeated oauth-token-expired alerts by enforcing a per-provider minimum notification interval.

- add a 12-hour default min notify interval to OAuthExpiryMonitor and track last notification time by provider
- skip dispatch when a provider was already notified within the configured window, even if expiry timestamps change
- clear per-provider notification timestamps when no OAuth providers are configured
- add regression coverage for 12-hour throttling and changed-expiry throttling behavior
- add a patch changeset for @runfusion/fusion documenting the notification throttle

Files changed:
 .changeset/fn-5778-oauth-notify-throttle.md        |  5 +++
 packages/engine/src/notification/__tests__/oauth-expiry-monitor.test.ts         | 40 ++++++++++++++++++++++
 packages/engine/src/notification/oauth-expiry-monitor.ts       | 15 ++++++++
 3 files changed, 60 insertions(+)

Fusion-Task-Id: FN-5778

Fusion-Task-Lineage: a9741101-e8da-4dae-ac7f-817e564b4f23
2026-05-31 11:55:11 -07:00
gsxdsm
e75c4dae28 fix(FN-5627): suppress ntfy notifications for transient merge failures the engine auto-recovers
Even with FN-5627's merger TOCTOU fix + transient-failure self-healing
sweep + safety-fallback auto-prerebase landed, the merger can still hit
transient failure classes (lease handoff races, brief same-SHA non-FF
advances) for tasks whose branches are particularly out-of-sync. The
self-healing sweep auto-recovers them within bounded budget \u2014 but each
individual failure cycle was firing a ntfy alarm before the recovery
cleared the failed state, producing user-facing alarm spam for tasks
that were never actually stuck.

Two layers of fix:

1. NotificationService.handleTaskUpdated now classifies task.error via
   the new shared classifyTransientMergeError helper before scheduling
   the deferred failure notification. Transient classes
   (lease-handoff-target-not-queued, spurious-concurrent-advance-same-sha)
   get logged as suppressed and never schedule a ntfy timer.

2. Defense-in-depth: fireDeferredFailureNotification re-classifies the
   error at dispatch time, so a failure scheduled before the suppression
   landed on a newer cycle still suppresses if the error matches a
   transient class.

The classifier itself moved from self-healing.ts to a new logger-free
transient-merge-error-classifier.ts module so consumers in
NotificationService don't pull createLogger through the import chain and
break test mocks of ../logger.js (per project-memory rule about new
modules using createLogger). self-healing.ts re-exports the symbol for
backward compatibility.

Log prefix for the recovery actions also changed from
'[FN-5627] Auto-recovering...' to 'Auto-recovered:' so that
NotificationService.maybeSuppressTransientFailedNotification's existing
/^Auto-recovered:/ log-prefix check cancels any already-scheduled failure
notification when the sweep runs mid-grace-window.

Tests (3 new):
- transient lease-handoff-target-not-queued failure NOT notified
- transient spurious-concurrent-advance-same-sha failure NOT notified
- genuine different-SHAs concurrent-advance still notifies (control)

Engine suite: 6166 tests pass.

Fusion-Task-Id: FN-5627
2026-05-28 15:39:23 -07:00
gsxdsm
200dda95dc feat(FN-5624): suppress transient task.json ENOENT with guard, logging, and
Implements graceful suppression of transient `task.json` ENOENT errors in the executor, logging a suppression signal and surfacing a banner in the UI, with test coverage for both the executor behavior and notification service. Documentation in `docs/architecture.md` and a changeset for `@runfusion/f

Fusion-Task-Id: FN-5624

Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
Fusion-Task-Id: FN-5624
2026-05-28 12:10:03 -07:00
gsxdsm
da34bd06e3 feat(FN-5595): add oauth relogin banner with validity logger
This merge implements an OAuth relogin banner feature (FN-5595) that displays in the dashboard when OAuth tokens expire. The feature includes a new `OAuthReloginBanner` component with styling and tests, an OAuth validity logger in the engine for tracking token state, and corresponding API route inte

Fusion-Task-Id: FN-5595

Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
Fusion-Task-Id: FN-5595
2026-05-27 21:31:12 -07:00
Fusion (runfusion.ai)
c31bf013df test(FN-5046): complete Step 3 — cover terminal-only failure notification behavior
Fusion-Task-Id: FN-5046
Fusion-Task-Lineage: 0748fd0e-70c3-44b9-a599-0df5255d4d60
2026-05-18 07:31:29 -07:00
Fusion (runfusion.ai)
7167eebe6b feat(FN-5046): complete Step 2 — add terminal-only failure suppression
Fusion-Task-Id: FN-5046
Fusion-Task-Lineage: 0748fd0e-70c3-44b9-a599-0df5255d4d60
2026-05-18 07:31:28 -07:00
Fusion (runfusion.ai)
8bcc381041 feat(FN-4905): complete Step 2 — add lifecycle refresh fallback
Fusion-Task-Id: FN-4905
Fusion-Task-Lineage: 677b6f08-90d7-4785-a0b8-195acc42f79e
2026-05-17 09:51:49 -07:00
Fusion (runfusion.ai)
5e159368dc feat(FN-4783): complete Steps 4-5 — provider rendering and engine wiring
Fusion-Task-Id: FN-4783
Fusion-Task-Lineage: 7a02897b-e303-4947-86b2-3dd7a343f653
2026-05-16 13:46:42 -07:00
Fusion (runfusion.ai)
b48f8f1fac feat(FN-4783): complete Step 2 — add OAuth expiry monitor
Fusion-Task-Id: FN-4783
Fusion-Task-Lineage: 7a02897b-e303-4947-86b2-3dd7a343f653
2026-05-16 13:46:42 -07:00
Fusion
b752307122 test(FN-4307): fix notification tests for strict task status typing
Fusion-Task-Id: FN-4307
Fusion-Task-Lineage: 00140a5a-af93-4905-8570-32d7ecb8a874
2026-05-13 15:58:49 -07:00
Fusion
6bf90f1cac feat(FN-4307): complete Step 2 — suppress transient failure surfacing
Fusion-Task-Id: FN-4307
Fusion-Task-Lineage: 00140a5a-af93-4905-8570-32d7ecb8a874
2026-05-13 15:58:48 -07:00
Fusion
74f069b57f feat(FN-4307): complete Step 1 — defer failed notifications
Fusion-Task-Id: FN-4307
Fusion-Task-Lineage: 00140a5a-af93-4905-8570-32d7ecb8a874
2026-05-13 15:58:48 -07:00
Fusion
0ee31db9e1 feat(FN-4332): complete Step 2 — defer failed notifications
Fusion-Task-Id: FN-4332
Fusion-Task-Lineage: c920ac71-906b-458d-b496-1b60540b58f3
2026-05-13 15:15:41 -07:00
Fusion
2a26b0d344 feat(FN-4178): add room message notification delivery
This merge implements room message notifications across the system, adding a core room event type, wiring the notification dispatcher to room activity, and delivering notifications via ntfy and webhook providers with updated settings UI and API routes.

Fusion-Task-Id: FN-4178
2026-05-12 17:04:33 -07:00
Fusion
2d46a882f0 feat(FN-4078): add ntfy access token support to notifications
Adds ntfy access token support to Fusion's notification system, wiring the token through the core settings schema, dashboard UI (SettingsModal), engine notifier, and notification pipeline, with corresponding tests across core, dashboard, and engine packages; also updates settings and storage documen

Fusion-Task-Id: FN-4078
2026-05-12 07:47:11 -07:00
Fusion
2d3af5b6ce fix(FN-3886): resolve peer agent names in mail and notifications
- Resolve participant display names in AgentDetailView mail tab for peer agents
- Update MailboxModal labeling to use readable agent names instead of raw IDs
- Propagate resolved agent names through notification service, notifier, and provider payloads
- Add and update dashboard/engine tests plus a changeset for @runfusion/fusion

Fusion-Task-Id: FN-3886
2026-05-09 14:58:56 -07:00
Fusion
7bf93e8501 fix(FN-3825): surface inline notification test feedback in settings
- Persist per-provider notification test results and render inline success/error feedback blocks
- Add ntfy "Test message notification" action wired to message-event test payload
- Keep toast notifications while also showing provider-specific status text with aria-live feedback
- Update SettingsModal tests to verify ntfy message-event call path and inline webhook/ntfy feedback rendering

Fusion-Task-Id: FN-3825
2026-05-09 11:02:12 -07:00
Fusion
fdf387b220 feat(FN-3800): add session switcher to chat header and mobile switcher
This merge delivers five features: a session switcher for the chat header with mobile-aware dropdown styling and proper ARIA state, mailbox notification events with deep-link highlighting to the unread task, a fix for org chart connector endpoints in wide subtrees, a correction to merge finalize so

Fusion-Task-Id: FN-3800
2026-05-08 22:25:47 -07:00
Fusion
68f34dd0f7 feat(FN-3008): notify when model settings fall back to defaults
Merges FN-3008 to add a "fallback-used" notification system: the engine now emits events when AI model fallbacks are triggered, dispatches notifications via ntfy/webhook providers, surfaces a session banner in the dashboard, and exposes a settings toggle to enable or disable these alerts.

Fusion-Task-Id: FN-3008
2026-05-02 19:55:54 -07:00
Fusion
8d606b45c5 feat(FN-2878): add webhook notification provider support
- Add webhook settings fields and defaults for enablement, URL, format, and event filtering
- Implement WebhookNotificationProvider with payload formatting support for generic, Slack, and Discord endpoints
- Extend NotificationService to manage both ntfy and webhook providers with live settings sync
- Export webhook notification types/providers through engine notification entry points
2026-04-28 14:25:56 -07:00
Fusion
e782636c4b feat(FN-2866): wire provider-backed notification service into engine
- Add notification service module with provider abstractions and ntfy provider implementation
- Refactor NtfyNotifier into a compatibility wrapper that delegates task-event delivery to NotificationService
- Initialize and stop NotificationService from ProjectEngine while preserving gridlock notifications via NtfyNotifier
- Export notification APIs from engine index and add focused unit coverage for provider, service, and project-engine wiring
2026-04-28 14:25:56 -07:00