Commit Graph

13380 Commits

Author SHA1 Message Date
ischindl
8cba8d3e92 fix(core): make TaskStore.emit override assignable to EventEmitter<TaskStoreEvents> signature (#3407)
## Problem
Dashboard typecheck fails with **TS2416** in `@fusion/core`'s
`TaskStore`:

```
Property 'emit' in type 'TaskStore' is not assignable to the same property in base type 'EventEmitter<TaskStoreEvents>'.
```

The `override emit<E extends string | symbol>(event, ...args)` generic
conflicts with the base class's generic `emit<K>(eventName: keyof
TaskStoreEvents | K, ...)`. This breaks the dashboard typecheck / CI
merge gate.

## Fix
Change the override to:

```ts
override emit(event: unknown, ...args: any[]): boolean {
  return EventEmitter.prototype.emit.call(this, event as string, ...args);
}
```

`event: unknown` remains assignable to the base's generic signature
while still forwarding non-typed runtime keys (`agent:log`,
`settings:updated`, …). Internal `EventEmitter.prototype.emit` calls
cast `event as string`. Behavior-preserving.

## Verification
- `@fusion/dashboard` `tsc --noEmit` → **PASS** (previously failed with
TS2416)
- `eslint` on touched file → clean
- Single-file change (`packages/core/src/store.ts`, +6/−3)

## Scope
No behavior change, no changesets required.

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

* **Bug Fixes**
* Improved task event handling to support a broader range of event
identifiers.
* Preserved cached-lane information for single-argument task update
events.
* Maintained support for custom and arbitrary event names without
disrupting existing behavior.
* Improved classification of workflow roles, session purposes, and
outcome-related status checks in lifecycle analysis, producing more
accurate findings and reducing misleading results.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2026-08-10 14:10:26 -10:00
gsxdsm
3143f9536f FN-8908: auto-recover terminal task failures
Recover generic terminal task failures through a bounded, durable retry budget before escalating them to operators.

- add fenced task-store recovery claims, retries, budget resets, and audit events
- defer terminal-failure notifications until recovery is exhausted while preserving a single escalation
- expose operator retry budget reset and cover recovery lifecycle behavior

Files changed:
 .../fn-8908-terminal-failure-auto-recovery.md      |   7 +
 AGENTS.md                                          |   1 +
 docs/agents.md                                     |   2 +
 docs/architecture.md                               |   2 +
 packages/cli/src/commands/task.ts                  |   2 +
 packages/cli/src/extension.ts                      |   2 +
 ...terminal-failure-auto-recovery-store.pg.test.ts | 108 +++++++++
 .../terminal-failure-auto-recovery.test.ts         |  60 +++++
 packages/core/src/index.gate.ts                    |   1 +
 packages/core/src/index.ts                         |   1 +
 packages/core/src/store.ts                         | 179 ++++++++++++++-
 .../core/src/task-store/archive-lifecycle-2.ts     |  15 ++
 packages/core/src/task-store/moves.ts              |  48 +++-
 packages/core/src/task-store/persistence.ts        |  18 +-
 packages/core/src/task-store/project-store-ops.ts  |   4 +-
 .../src/task-store/workflow-task-create-ops.ts     |   4 +-
 packages/core/src/tasks/index.ts                   |   1 +
 .../src/tasks/terminal-failure-auto-recovery.ts    | 114 ++++++++++
 packages/core/src/types/task/task-core.ts          |  24 ++
 .../src/routes/register-task-workflow-routes.ts    |   2 +
 ...-healing-terminal-failure-auto-recovery.test.ts | 199 ++++++++++++++++
 .../__tests__/notification-service.test.ts         |  86 ++++++-
 .../__tests__/task-wedge-notification.test.ts      |   2 +-
 .../src/notification/notification-service.ts       | 103 +++++++--
 .../src/notification/task-wedge-notification.ts    |  30 ++-
 packages/engine/src/self-healing.ts                | 251 ++++++++++++++++++++-
 packages/engine/src/util/run-audit.ts              |   7 +
 27 files changed, 1240 insertions(+), 33 deletions(-)

Fusion-Task-Id: FN-8908

Fusion-Task-Lineage: 99e96b16-0306-41f1-87da-8623d69735f7

Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
2026-08-10 16:53:51 -07:00
gsxdsm
55fd20e5f8 fix(engine): bound spec-drift reconciliation so boot cannot exhaust the connection pool
Fusion wedged on "starting" and never brought the engine up. The dashboard bound
the migration holding server on 4040, then every query behind it failed with
"sorry, too many clients already", so the card never progressed and the
supervisor crash-looped.

Root cause: each spec-drift reconcile costs a DEDICATED PostgreSQL connection.
persist -> appendSpecDriftReport -> withPlanningLifecycleLock opens its own
postgres(directUrl, { max: 1 }) session, because the planning advisory lock is
session-scoped and deliberately fences a stale report against a newer plan.

enqueue() released every id straight into its own microtask, and project-engine
enqueues every task at runtime-boundary setup (listTasks includeArchived). On a
1,082-task project that opened ~1,082 lock sessions simultaneously against
max_connections = 500. The cluster saturated ~25s into boot and stayed saturated.

The flat 1s retry then made it self-sustaining rather than transient: once
saturated, every task failed for the same shared reason and re-armed in lockstep
once per second, re-opening the whole fleet of sessions and pinning the very
resource it was waiting on. Measured 4,777 lock sessions in 17 seconds.

Fix, contained to the reconciler — the advisory lock and its fencing semantics
are load-bearing and unchanged:
- concurrency bound (maxConcurrent, default 4) drained by a fair
  insertion-ordered pump, so fan-out can no longer exceed a known connection cost
- per-task in-flight dedupe; two passes on one task would contend on that task's
  own advisory lock while holding two connections
- exponential backoff with jitter capped at 60s, and retries re-enter through
  enqueue so a retry storm is bounded by the same limit as a first pass

Verified against the real 1,082-task project: connections stay flat at 3-10
across a 70s boot that previously reached 1,109 and saturated, and the engine
boots through to executing tasks and shuts down cleanly.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-10 16:44:19 -07:00
gsxdsm
67cdb33a92 chore: gitignore the knowledge-graph build artifact
`.fusion-knowledge/` is `fn knowledge-graph build` output (FN-8920 memory epic):
~85MB of generated JSON, rewritten on every build. edges.json alone is 43MB,
past GitHub's 50MB warning threshold.

It is deterministic and fully regenerable from source — the manifest's SHA-256
fingerprints drive incremental rebuilds — so tracking it preserves nothing a
rebuild cannot reproduce, while adding irreversible, compounding history bloat to
every clone and CI checkout.

docs/knowledge-graph.md previously left this to the operator ("it is not
ignored"); that guidance is updated here so the doc and .gitignore agree. An
operator who explicitly wants a snapshot in history can still force one with
`git add -f .fusion-knowledge/graph`.

Nothing under the path was ever tracked, so no index purge is needed.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-10 13:49:37 -07:00
gsxdsm
7b3bd75268 fix(engine): wire the Plan Review replan cap and tombstone its dead predecessor
Investigating the "dead cap" turned up the opposite of what it looked like, plus a
worse problem next to it.

The Plan Review replan loop was NOT unbounded. U3 re-owned the cap-park in the
graph: requestPreMergeOptionalStepFix parks via parkPlanReviewReplanCapExhausted
at awaiting-approval with reason plan-review-replan-cap, on both an explicit
finite budget and the unbounded default. That capability has been live throughout.

What was actually dead:

1. PLAN_REVIEW_GATE_REPLAN_CAP = 8 — an unread constant belonging to the
out-of-graph triage gate (runPlanReviewBeforeExecution) that U10/R4 deleted. Its
companion column Task.planReviewReplanCount was persisted, serialized and reset
but never incremented or compared. A constant and a column that look like a live
safety ceiling while enforcing nothing are worse than no ceiling: they answer "is
this loop bounded?" with a confident yes. Deleted, ratcheted in
legacy-tombstones.test.ts, and the column documented as legacy/never-written with
the live owner named.

2. planReviewReplanCap — an operator-facing setting, declared, validated,
documented in settings-reference.md and editable in the Workflow Editor, that
NOTHING read. Lowering it changed nothing. The unbounded backstop was instead
hardcoded to PLAN_REVIEW_FEEDBACK_HISTORY_LIMIT — a bound on how much reviewer
PROSE is replayed into the next planning prompt, whose own comment says it is
"bounded independently of persistence and retry accounting". Two unrelated
concerns shared one number, so trimming prompt history would have silently
tightened a safety ceiling.

The backstop now resolves from the setting, defaulting to the new
DEFAULT_PLAN_REVIEW_REPLAN_CAP = 15 — the previously-effective value, so this is a
pure re-wiring rather than a silent behavior change. The existing 15-attempt
regression test passes unchanged, which is the evidence for that. 0 is honored as
park-on-first-REVISE. An explicit planReviewMaxRevisions / node maxRevisions
budget remains a stricter, earlier gate.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-10 13:39:55 -07:00
gsxdsm
c05d44d44a fix(engine): bound planning retries and cap the planning turn
Three changes to the triage planning path.

1. Unclassified planning failures are bounded. specifyTask's catch-all branch —
the one reached by every error the classifiers above do not recognize — restored
the card's claimable status and wrote nothing else: no counter, no
nextRecoveryAt, no park. Triage rediscovery re-admitted the card on the very next
poll, and replaceActiveTaskWorkflowContinuation replaced the terminal work item
with a fresh one carrying no attempt count, so nothing recorded that the task had
already failed N times. It now consumes the same recoveryRetryCount/nextRecoveryAt
budget the transient branch uses (MAX_RECOVERY_RETRIES = 3, 60s/120s/300s jittered
backoff) and parks status:"failed" with a PLANNING_FAILED_EXHAUSTED: error once
spent — status:"failed" is what suppresses rediscovery. Classifying one error
string fixes one symptom; this budget is what makes the NEXT unrecognized error
fail safely instead of looping for a day.

2. The planning turn has a ceiling. Fusion set no timeout on it at all:
workflowStepTimeoutMs covers pre-merge workflow steps only, and the provider SDK's
300s APIConnectionTimeoutError caps time-to-first-byte and is cleared once headers
arrive, after which the stream is uncapped. configureHttpDispatcher, which would
install undici idle timeouts, is only called from pi's CLI entrypoints and never
in the in-process engine. Observed consequence: single attempts ran to 126 minutes,
with failed-attempt durations spread smoothly from 1 to 126 min and no clustering —
the signature of nothing enforcing a bound. New workflow-native planningTimeoutMs
(default 90 min) aborts the session; the failure consumes one bounded attempt.

The default is deliberately generous rather than tight. Successful planning work
items measured over 7 days ran p50 12.7 / p90 39.5 / p99 105.7 minutes, so a
tighter bound would abort legitimate plans and pay for the restart — the churn
this work exists to remove. It bounds hung turns, not slow ones.

3. [event:task:moved] executor tracing dropped from log to debug. It fires on
every dispatch, rebound, requeue, archive and self-healing move across every task,
which made it the loudest line in engine output and buried operator-actionable
events. No test pins the level; the information remains at debug.

Also fixes a test break shipped in 963dba6f80: the review blocking-severity
settings landed inside BUILTIN_REVIEW_REVISION_SETTINGS, whose contents
builtin-workflow-settings-triage.test.ts asserts exactly.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-10 13:27:10 -07:00
gsxdsm
e0af6ddd93 FN-8952: derive Quick Add Save smoke fixtures from locales
Keep 300px Quick Add Save browser smoke coverage synchronized with shipped translations.

- Derive board and list Save fixtures from supported locale catalogs.
- Escape fixture labels and reject missing Save translations.
- Add fast fixture parity, derivation, and escaping coverage.

Files changed:
 docs/testing.md                                    |   3 +
 .../__tests__/browser-layout-smoke-fixture.test.ts |  75 +++++++++++++-
 .../dashboard/scripts/browser-layout-smoke.mjs     | 110 ++++++++++++++-------
 3 files changed, 147 insertions(+), 41 deletions(-)

Fusion-Task-Id: FN-8952
Fusion-Task-Lineage: 31daec25-5d9a-43bb-9ccf-667702006e76
Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
2026-08-10 12:38:00 -07:00
gsxdsm
878db6dca7 FN-8951: repair script-test governance drift
Keep test-shard timing governance aligned with the current workspace and workflow seams.

- Add a safe timing-snapshot pruning mode with coverage.
- Align Todo plugin Vitest isolation and Docker dependency manifests.
- Refresh workflow reliability evidence and remove deleted test timings.

Files changed:
 Dockerfile                                         |  7 ++-
 docs/testing.md                                    |  9 ++-
 plugins/fusion-plugin-todos/vitest.config.ts       | 26 ++++++--
 scripts/__tests__/ci-test-shard-timings.test.mjs   | 71 ++++++++++++++++++++++
 scripts/ci-test-shard.mjs                          | 65 ++++++++++++++++++--
 .../lib/workflow-reliability-release-check.json    | 22 +++----
 scripts/test-timings.json                          | 21 -------
 7 files changed, 175 insertions(+), 46 deletions(-)

Fusion-Task-Id: FN-8951

Fusion-Task-Lineage: fbf7e79f-4cb2-43e2-9982-09f3f94de70d

Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
2026-08-10 12:09:20 -07:00
gsxdsm
81139dbbd0 fix(engine): classify provider request timeouts as transient
"Request timed out." is the literal default message of the Anthropic and OpenAI
SDKs' APIConnectionTimeoutError, surfaced to Fusion by checkSessionError after
pi-coding-agent exhausts its in-session retries. It matched none of the
connection-scoped timeout patterns, which deliberately excluded "general
timeouts", so it fell through to specifyTask's generic failure branch — the one
that restores status: null and writes no counter, no nextRecoveryAt, and no park.
Triage rediscovery then re-admitted the card on the very next poll, forever.

Measured before this change: 48 "Specification failed: Request timed out." events
across 10 tasks in 30 hours with zero backoff between attempts. FN-8950 alone
burned 8 consecutive attempts over ~8 hours and never reached implementation.
Across 2 days, 91 failed planning attempts averaged 33 minutes each — ~50 hours of
wall-clock producing nothing, 24% of all planning time.

Classifying these as transient routes them into the bounded recovery policy
(MAX_RECOVERY_RETRIES = 3, 60s/120s/300s jittered backoff) already used by the
connection-level patterns, so a provider blip costs three spaced retries instead
of an unbounded loop.

The pattern is anchored to "request timed out" rather than a bare timeout match:
agent log prose and verification output legitimately contain "timed out"
("BuildKit timed out", "stuck-kill unwind timeout"), and a broad pattern would
reclassify real permanent failures as retryable — the mistake the connection-only
rule was written to avoid. Regression tests pin both directions.

This does not affect model fallback, which pi decides internally and Fusion only
observes.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-10 12:01:25 -07:00
gsxdsm
00ddafd5fe FN-8845: persist deterministic spec alignment
Persist approved-plan drift alignment on linked mission features.

- Store spec alignment across PostgreSQL and SQLite mission feature projections
- Reconcile and render durable alignment instead of browser-side task joins
- Preserve migration identities and cover drift persistence regressions

Files changed:
 docs/architecture.md                               |  2 +
 docs/missions.md                                   |  2 +-
 .../core/src/__tests__/planner/spec-lock.test.ts   |  1 +
 .../src/__tests__/postgres/schema-applier.test.ts  |  9 ++++-
 .../async-stores/async-mission-store-queries.ts    |  5 +++
 packages/core/src/missions/mission-store.ts        |  9 ++++-
 packages/core/src/missions/mission-types.ts        | 10 +++++
 packages/core/src/planner/spec-lock.ts             |  7 +++-
 .../0053_mission_feature_spec_alignment.sql        |  3 ++
 packages/core/src/postgres/schema-applier.ts       | 13 +++++-
 packages/core/src/postgres/schema/project.ts       |  2 +
 packages/dashboard/app/api/missions/missions.ts    |  2 +
 .../dashboard/app/components/MissionManager.tsx    | 40 ++++++------------
 packages/dashboard/app/components/mission-types.ts |  2 +
 .../src/__tests__/plan-approval-status.pg.test.ts  |  4 +-
 .../src/__tests__/mission-feature-sync.test.ts     | 37 ++++++++++++++++-
 .../src/__tests__/spec-drift-reconciler.test.ts    | 14 +++++++
 packages/engine/src/missions/mission-autopilot.ts  | 17 +++++---
 .../engine/src/missions/mission-feature-sync.ts    | 47 +++++++++++++++++++++-
 packages/engine/src/project-engine.ts              |  3 +-
 packages/engine/src/scheduler.ts                   | 40 +++++++++++-------
 packages/engine/src/spec-drift-reconciler.ts       |  5 +++
 22 files changed, 215 insertions(+), 59 deletions(-)

Fusion-Task-Id: FN-8845

Fusion-Task-Lineage: d4a30472-f1c3-41ba-a61b-3f2be1ad32ab

Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
2026-08-10 11:50:56 -07:00
gsxdsm
963dba6f80 feat: gate review verdicts on finding severity and preserve remediation sessions
Review remediation loops were the dominant cost of task wall-clock: over 14 days,
tasks with >=5 post-review fix rounds were 22% of tasks but consumed 78% of all
task active time, and 311 of 331 recorded findings were spec-internal-consistency
complaints that changed no delivered behavior.

Two causes compounded. Plan/Code Review remediation was unbounded by default, and
the review policy ordered a full re-derivation of the artifact after every edit
("distrust the edit ... fresh holistic pass"), so each round surfaced a fresh crop
of previously-acceptable observations as new blockers.

Make the already-persisted WorkflowReviewFinding.severity load-bearing instead of
decorative: a REVISE only blocks when it carries a finding at or above the review
kind's threshold (plan: P0+P1, code: P0). Non-blocking findings are still parsed,
persisted, and handed to the implementer as advisory notes in PROMPT.md. Fails
closed — a REVISE with no findings, or with any unclassified finding, still blocks,
so prose-only and custom reviewers keep full blocking power. The gate only ever
relaxes a verdict, never promotes one.

Reviewer prompts now request the structured findings schema (Plan Review emitted
none before), define severity by consequence as P0/P1/P2, omit nits entirely rather
than filing them as low-severity findings, and use an incremental re-review contract.
Remediation renders findings grouped by priority and sanctions an explicit decline
with rationale, so a disputed finding has a terminal state.

Also preserve the implementation session across a review bounce: sendTaskBackForFix
no longer nulls sessionFile when preserving resume state, and the executor's finally
no longer clears it on a review handoff. Remediation rounds continue the conversation
instead of re-reading the repo and re-deriving the change they just wrote. The resume
prompt now directs a PROMPT.md re-read, without which a resumed agent would never see
the new findings.

New per-workflow settings planReviewBlockingSeverity / codeReviewBlockingSeverity;
set either to "any" to restore the previous behavior.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-10 11:28:32 -07:00
gsxdsm
e6b6223d30 fix(engine): stop per-poll symbol-lock renewal log and activityLog spam
Renewal runs every poll for every implementation-column task with declared
symbols, and a lost lock never recovers by renewing — renewSymbolLocks reports
the same lost set on each pass, so the warning and its store.logEntry companion
repeated forever: log-pane spam plus unbounded activityLog growth for a stuck
task. The two error paths had the same shape on any persistent failure.

Extract the executor's suppression into a shared createRepeatSuppressedLog and
use it in both: first occurrence per task/signature logs at full level, repeats
drop to debug(), a changed lost set or error message logs again, and a clean
renewal clears the memo. The logEntry write is gated on the same decision.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-10 10:33:35 -07:00
gsxdsm
a2deae041a fix(engine): stop per-poll executor dispatch-blocked log spam
The unmet-dependency and ephemeral-disabled pre-dispatch gates re-run on every
dispatch attempt for a blocked task but only change state on the first, so every
later pass re-logged the same line at default level and drowned the log pane.

Route both through logDispatchBlockedOnce: first block per task/reason logs at
log(), identical repeats drop to debug() (FUSION_DEBUG=executor), a changed
reason logs again, and the marker clears when the gate passes.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-10 10:11:11 -07:00
gsxdsm
936dd1ea96 FN-8924: retain safe age gate for merge recovery
Document why interrupted merge recovery retains its age-based admission gate.

- Record why merger logs cannot establish task ownership or orphan status.
- Pin non-owner recovery behavior when a merging task was recently updated.

Files changed:
 docs/self-healing-backward-move-audit.md           | 8 +++++++-
 packages/engine/src/__tests__/self-healing.test.ts | 9 ++++++++-
 2 files changed, 15 insertions(+), 2 deletions(-)

Fusion-Task-Id: FN-8924

Fusion-Task-Lineage: 19e7f803-21fe-43fb-8ca6-eecf8ff61df4

Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
2026-08-10 09:09:04 -07:00
gsxdsm
82b5d78bf8 FN-8945: invalidate approved lineage fingerprints
Invalidate stale approval evidence atomically when a parent task leaves lineage.

- Lock and revalidate lineage children before parent deletion or archival.
- Clear affected approvals, append invalidation evidence, and reconcile cleared children.
- Cover lifecycle races, evidence collisions, and async archive/delete paths.

Files changed:
 .changeset/fn-8945-lineage-approval-invalidation.md       |   7 +
 docs/architecture.md                               |   6 +
 .../lineage-approval-invalidation.pg.test.ts       | 379 +++++++++++++++++++++
 .../planning-lifecycle-advisory-lock.pg.test.ts    |  58 ++++
 .../postgres/runtime-lifecycle-async.test.ts       |   4 +
 .../__tests__/postgres/taskstore-lifecycle.test.ts |   3 +-
 packages/core/src/postgres/advisory-locks.ts       | 164 ++++++---
 packages/core/src/store.ts                         |  26 +-
 .../core/src/task-store/archive-lifecycle-2.ts     | 123 +++++--
 .../src/task-store/async/async-archive-lineage.ts  |  27 +-
 .../core/src/task-store/async/async-lifecycle.ts   |  89 +--
 .../task-store/lineage-approval-invalidation.ts    | 168 +++++++++
 12 files changed, 939 insertions(+), 115 deletions(-)

Fusion-Task-Id: FN-8945

Fusion-Task-Lineage: be7dd6c8-5411-4b0e-8734-9fe63b9d1d9f

Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
2026-08-10 08:48:47 -07:00
gsxdsm
0fc6f3d849 FN-8946: enable attributed agent mission status updates
Enable authorized agents to update feature and mission statuses with transactional, attributed audit events.

- Add mission and feature status tools to the Fusion extension and engine allowlists.
- Record bounded actor, reason, and hierarchy metadata for status transitions across every writer.
- Guard linked feature transitions and document the agent-facing workflow.

Files changed:
 .changeset/fn-8946-mission-status-writes.md        |   7 +
 docs/missions.md                                   |   7 +-
 packages/cli/skill/fusion/SKILL.md                 |   2 +-
 .../cli/skill/fusion/references/extension-tools.md |  20 +++
 .../skill/fusion/references/fusion-capabilities.md |   2 +
 packages/cli/src/__tests__/extension.test.ts       |  46 ++++++
 packages/cli/src/extension.ts                      |  27 +++
 .../mission-status-event-metadata.test.ts          |  50 ++++++
 .../__tests__/postgres/mission-store.pg.test.ts    | 160 +++++++++++++++++-
 .../core/src/async-stores/async-mission-store.ts   | 182 +++++++++++++++------
 packages/core/src/index.ts                         |   5 +
 packages/core/src/missions/mission-store.ts        |   7 +-
 packages/core/src/missions/mission-types.ts        |  99 +++++++++--
 .../src/__tests__/chat-toolset-permissions.test.ts |  24 +++
 packages/dashboard/src/mission-routes.ts           |   3 +-
 .../src/__tests__/agent-mission-tools.test.ts      |  42 ++++-
 .../src/__tests__/heartbeat-executor.test.ts       |   4 +-
 .../workflow-step-readonly-allowlist.test.ts       |   2 +
 packages/engine/src/agent-tools.ts                 |  18 ++
 .../engine/src/execution/gating-classifications.ts |   2 +
 20 files changed, 635 insertions(+), 74 deletions(-)

Fusion-Task-Id: FN-8946

Fusion-Task-Lineage: 473cc0e0-e632-48b3-ac84-adaaeb81db4b

Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
2026-08-10 07:30:42 -07:00
gsxdsm
a5e1b0efc2 docs: add docs index back-link to knowledge-graph.md
Missing [← Docs index](./README.md) back-link per convention for all
indexed docs. Added during ambient docs audit.
2026-08-10 06:23:35 -07:00
gsxdsm
27cb2d2621 FN-8921: add deterministic knowledge graph tooling
Add a committable, provenance-tagged knowledge graph layer with CLI generation and query support.

- Extract TypeScript, Markdown, and FNXC knowledge into deterministic graph nodes and edges.
- Persist recoverable graph artifacts outside ignored Fusion state and expose build/query CLI commands.
- Document configuration and add core and CLI coverage for graph structure, serialization, and recovery.

Files changed:
 .changeset/fn-8921-knowledge-graph.md              |   7 +
 .gitattributes                                     |   3 +
 AGENTS.md                                          |   1 +
 docs/README.md                                     |   2 +
 docs/cli-reference.md                              |   4 +
 docs/knowledge-graph.md                            |  37 +++++
 docs/settings-reference.md                         |   4 +
 docs/storage.md                                    |   2 +
 packages/cli/package.json                          |   3 +-
 .../__tests__/knowledge-graph-bundle-shape.test.ts |   4 +
 .../src/__tests__/knowledge-graph-command.test.ts  | 115 ++++++++++++++
 packages/cli/src/bin.ts                            |  19 +++
 packages/cli/src/commands/knowledge-graph.ts       |  79 ++++++++++
 packages/cli/tsup.config.ts                        |   2 +
 packages/core/package.json                         |   4 +-
 packages/core/src/config/settings-schema.ts        |   2 +
 packages/core/src/index.ts                         |   1 +
 .../__tests__/derive-modules.test.ts               |  11 ++
 .../__tests__/extract-file-composition.test.ts     |  20 +++
 .../knowledge-graph/__tests__/extract-fnxc.test.ts |  33 ++++
 .../__tests__/extract-markdown.test.ts             |  21 +++
 .../__tests__/extract-typescript.test.ts           |  30 ++++
 .../__tests__/file-discovery.test.ts               |  26 ++++
 .../graph-artifact-not-gitignored.test.ts          |  15 ++
 .../__tests__/graph-builder-equivalence.test.ts    |  76 ++++++++++
 .../__tests__/graph-builder-incremental.test.ts    |  52 +++++++
 .../__tests__/graph-identity.test.ts               |  11 ++
 .../knowledge-graph/__tests__/graph-query.test.ts  |  13 ++
 .../__tests__/graph-serialization.test.ts          |  29 ++++
 .../__tests__/graph-store-recovery.test.ts         | 106 +++++++++++++
 .../__tests__/resolve-imports.test.ts              |  10 ++
 .../core/src/knowledge-graph/derive-modules.ts     |   4 +
 packages/core/src/knowledge-graph/extract-file.ts  |   6 +
 packages/core/src/knowledge-graph/extract-fnxc.ts  | 168 +++++++++++++++++++++
 .../core/src/knowledge-graph/extract-markdown.ts   |   9 ++
 .../core/src/knowledge-graph/extract-typescript.ts | 107 +++++++++++++
 .../core/src/knowledge-graph/file-discovery.ts     |  85 +++++++++++
 packages/core/src/knowledge-graph/graph-builder.ts | 141 +++++++++++++++++
 .../core/src/knowledge-graph/graph-manifest.ts     |   4 +
 packages/core/src/knowledge-graph/graph-query.ts   | 126 ++++++++++++++++
 .../src/knowledge-graph/graph-serialization.ts     | 134 ++++++++++++++++
 packages/core/src/knowledge-graph/graph-store.ts   |  97 ++++++++++++
 packages/core/src/knowledge-graph/graph-types.ts   |  54 +++++++
 packages/core/src/knowledge-graph/index.ts         |  14 ++
 .../core/src/knowledge-graph/resolve-imports.ts    |   4 +
 packages/core/src/types/settings/settings-scope.ts |   2 +
 pnpm-lock.yaml                                     |  12 +-
 47 files changed, 1700 insertions(+), 9 deletions(-)

Fusion-Task-Id: FN-8921

Fusion-Task-Lineage: 7014d0f1-fc47-454b-afe5-5f0d9b229f33

Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
2026-08-10 06:15:17 -07:00
gsxdsm
00e369711c FN-8949: add dead mock specifier guard
Prevent silent dead engine test mocks and restore renamed-lane coverage.

- Add a ratcheting test that detects unresolved relative vi.mock specifiers.
- Update self-healing test seams for moved modules and queue transitions.
- Document mock-specifier and store-fake failure patterns.

Files changed:
 .../dead-vi-mock-specifiers-fail-silently.md       |  57 ++++++
 ...e-defects-that-masquerade-as-production-bugs.md |  12 ++
 docs/testing.md                                    |   5 +
 .../self-healing-query-filter-blindness.test.ts    |  42 ++++-
 .../__tests__/vi-mock-specifiers-resolve.test.ts   | 199 +++++++++++++++++++++
 5 files changed, 306 insertions(+), 9 deletions(-)

Fusion-Task-Id: FN-8949

Fusion-Task-Lineage: a1079d1c-a2a9-4ac2-8245-1db434468405

Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
2026-08-10 04:54:55 -07:00
gsxdsm
5573d042a9 FN-8922: add persistent memory recall store
Add PostgreSQL-backed recall records for durable project memory retrieval.

- add recall record types, storage, search, deduplication, and prompt instructions
- migrate project schemas and expose recall APIs from core memory modules
- document plugin and storage contracts, with unit and PostgreSQL coverage

Files changed:
 .changeset/fn-8922-memory-recall-store.md          |   7 +
 docs/memory-plugin-contract.md                     |  10 +
 docs/storage.md                                    |   4 +
 .../__tests__/memory-recall-dedup-search.test.ts   |  10 +
 .../__tests__/memory-recall-instructions.test.ts   |  62 +++++
 .../postgres/memory-recall-store.pg.test.ts        | 296 +++++++++++++++++++++
 .../src/__tests__/postgres/schema-applier.test.ts  |  15 +-
 packages/core/src/index.ts                         |   1 +
 packages/core/src/memory/index.ts                  |   1 +
 packages/core/src/memory/project-memory.ts         |  33 ++-
 packages/core/src/memory/recall/index.ts           |  10 +
 packages/core/src/memory/recall/recall-dedup.ts    |  35 +++
 .../core/src/memory/recall/recall-instructions.ts  |  59 ++++
 packages/core/src/memory/recall/recall-search.ts   |  20 ++
 packages/core/src/memory/recall/recall-store.ts    |  51 ++++
 packages/core/src/memory/recall/recall-types.ts    |  14 +
 .../0052_fn_8922_memory_recall_records.sql         |  17 ++
 packages/core/src/postgres/schema-applier.ts       |  13 +-
 packages/core/src/postgres/schema/project.ts       |  10 +-
 19 files changed, 648 insertions(+), 20 deletions(-)

Fusion-Task-Id: FN-8922

Fusion-Task-Lineage: bb98f048-6fcf-46d9-bd1f-09294c2dc005

Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
2026-08-10 04:46:10 -07:00
gsxdsm
0fbeba50d1 FN-8937: rescue project engine test quarantine
Rescue the project engine suite by making subprocess watchdog behavior deterministic.

- Capture real timer APIs for subprocess watchdogs and isolate failure ownership.
- Mock integration-branch resolution to prevent host git during lifecycle tests.
- Add watchdog regression coverage and remove the expired quarantine exclusion.

Files changed:
 docs/testing.md                                    |   3 +
 packages/core/src/__test-utils__/vitest-setup.ts   |  74 ++++++++++-
 .../__tests__/subprocess-guard-fake-timers.test.ts | 140 +++++++++++++++++++++
 .../engine/src/__tests__/project-engine.test.ts    |  63 +++++++---
 packages/engine/vitest.config.ts                   |  12 +-
 scripts/lib/test-quarantine.json                   |   8 +-
 6 files changed, 265 insertions(+), 35 deletions(-)

Fusion-Task-Id: FN-8937

Fusion-Task-Lineage: 9fe166b5-b101-4683-bb2b-4855ee73df10

Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
2026-08-10 03:50:58 -07:00
gsxdsm
08a3f2851b FN-8919: harden stale agent link recovery
Normalize missing task lookup errors so stale durable-agent links do not halt self-healing sweeps.

- Treat thrown not-found and deleted task lookups as stale links.
- Isolate transient task lookup failures to their affected agent.
- Add recovery regression coverage and a patch changeset.

Files changed:
 .changeset/fn-8919-agent-link-sweep-fail-open.md   |  7 ++
 .../self-healing-agent-link-drift.test.ts          | 58 +++++++++++++--
 .../self-healing-path-utils-task-miss.test.ts      | 51 +++++++++++++
 packages/engine/src/__tests__/self-healing.test.ts | 84 +++++++++++++++++++++-
 .../engine/src/healing/self-healing-path-utils.ts  | 34 ++++++++-
 packages/engine/src/self-healing.ts                | 57 +++++++++++----
 6 files changed, 272 insertions(+), 19 deletions(-)

Fusion-Task-Id: FN-8919

Fusion-Task-Lineage: 6dca2c1d-8ff9-47fb-b6c1-24f5af57b499

Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
2026-08-10 03:31:50 -07:00
gsxdsm
313eea1461 FN-8935: make Agents Overview scrollable on mobile
Make long Active Agents lists reachable within constrained mobile and short desktop layouts.

- Establish a bounded, touch-friendly Active Agents scroll owner.
- Add unit and Chromium layout coverage for populated and empty overview states.
- Document the required browser smoke coverage and add a patch changeset.

Files changed:
 .../fn-8935-agents-overview-mobile-scroll.md       |  7 ++
 docs/testing.md                                    |  2 +
 .../__tests__/browser-layout-smoke-fixture.test.ts | 26 +++++-
 .../dashboard/app/components/AgentsOverviewBar.css | 14 ++++
 .../AgentsOverviewBar.mobile-scroll.test.tsx       | 96 ++++++++++++++++++++++
 .../dashboard/scripts/browser-layout-smoke.mjs     | 86 +++++++++++++++++++
 6 files changed, 230 insertions(+), 1 deletion(-)

Fusion-Task-Id: FN-8935

Fusion-Task-Lineage: 43b793b9-3629-45a8-b6c1-d8100467e960

Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
2026-08-10 03:24:37 -07:00
gsxdsm
e610c72034 FN-8943: reconcile spec-lock divergence history
Preserve prior divergence when a task is re-locked after plan changes.

- Persist immutable spec locks, current-plan evidence, and drift reports.
- Reconcile retained divergence into re-approved alignment state across engine, API, and dashboard views.
- Fence Plan Review acceptance and schema upgrades while retaining migration identity parity.

Files changed:
 .changeset/fn-8845-spec-lock-drift-report.md       |   7 +
 .changeset/fn-8943-spec-lock-divergence.md         |   7 +
 docs/architecture.md                               |  17 +-
 docs/dashboard-guide.md                            |   3 +
 docs/missions.md                                   |   4 +
 .../core/src/__tests__/planner/spec-lock.test.ts   | 158 ++++++++++++++
 .../src/__tests__/postgres/schema-applier.test.ts  |  30 ++-
 .../postgres/task-dependency-mutation.pg.test.ts   |  74 +++++++
 packages/core/src/index.gate.ts                    |   4 +
 packages/core/src/index.ts                         |   4 +
 packages/core/src/planner/drift-report.ts          | 152 +++++++++++++
 packages/core/src/planner/spec-lock.ts             | 182 ++++++++++++++++
 .../migrations/0050_spec_lock_drift_report.sql     |  29 +++
 .../0051_spec_lock_source_revision_bigint.sql      |   3 +
 packages/core/src/postgres/schema-applier.ts       |  25 ++-
 packages/core/src/postgres/schema/project.ts       |  13 ++
 packages/core/src/store.ts                         | 242 ++++++++++++++++++++-
 .../core/src/task-store/branch-and-pr-entities.ts  |  32 ++-
 packages/core/src/task-store/project-store-ops.ts  |  34 ++-
 packages/core/src/task-store/task-update.ts        |  66 +++++-
 packages/core/src/task-store/update-task-deps.ts   |  31 ++-
 packages/dashboard/app/api.ts                      |   3 +
 packages/dashboard/app/api/tasks/tasks.ts          |  22 ++
 .../dashboard/app/components/MissionManager.css    |  21 ++
 .../dashboard/app/components/MissionManager.tsx    |  41 +++-
 .../dashboard/app/components/TaskDetailModal.css   |  26 +++
 .../dashboard/app/components/TaskDetailModal.tsx   |  62 +++++-
 .../__tests__/TaskDetailModal.spec-lock.test.tsx   |  80 +++++++
 .../__tests__/TaskDetailModal.test-helpers.ts      |   2 +
 .../src/__tests__/plan-approval-status.pg.test.ts  |  81 ++++++-
 .../src/routes/register-task-workflow-routes.ts    |  59 ++++-
 .../src/__tests__/mission-feature-sync.test.ts     |  15 +-
 .../src/__tests__/spec-drift-reconciler.test.ts    | 108 +++++++++
 .../engine/src/executor/execute-workflow-graph.ts  |  49 ++++-
 .../engine/src/missions/mission-feature-sync.ts    |  55 ++++-
 packages/engine/src/project-engine.ts              |  33 +++
 packages/engine/src/spec-drift-reconciler.ts       | 105 +++++++++
 packages/engine/src/triage.ts                      |  29 +++
 38 files changed, 1842 insertions(+), 66 deletions(-)

Fusion-Task-Id: FN-8943

Fusion-Task-Lineage: 2a7f8a38-99aa-4c4b-8c36-41d14c466d21

Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
2026-08-10 02:56:58 -07:00
gsxdsm
6cf95433bf FN-8928: evict flaky workflow IR PG gate canary
Remove the flaky sync-workflow-IR PostgreSQL canary from the blocking merge gate while preserving non-blocking coverage.

- Remove the default workflow-IR PostgreSQL test from the gate canary script.
- Update gate-policy coverage expectations and flake-eviction documentation.
- Record the observed setup-hook timeout and retained regression coverage.

Files changed:
 .../suite-only-flakes-observed-register.md         | 25 +++++++++++++--
 docs/testing.md                                    |  6 ++--
 packages/core/package.json                         |  2 +-
 .../sync-workflow-ir-is-always-default.pg.test.ts  |  6 ++++
 .../__tests__/engine-vitest-gate-policy.test.mjs   | 37 +++++++++++-----------
 5 files changed, 51 insertions(+), 25 deletions(-)

Fusion-Task-Id: FN-8928

Fusion-Task-Lineage: b725ba1a-fb33-4d49-89b4-277a64246cdd

Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
2026-08-10 02:32:24 -07:00
gsxdsm
5b9e6643d6 FN-8869: inherit project model overrides for role agents
Role-based permanent agents now consistently use their effective project model and thinking settings.

- Resolve permanent-agent model and thinking inheritance by workflow role in core.
- Apply inherited settings to Agent views, Chat sessions, routes, and heartbeat sessions.
- Cover role-specific inheritance and preserve the lifecycle-column ratchet classification.

Files changed:
 .changeset/fn-8869-role-agent-project-model-override.md   |   7 ++
 docs/agents.md                                     |   9 +-
 docs/settings-reference.md                         |   4 +-
 packages/core/src/__tests__/agent-effective-model.test.ts    |  97 +++++++++++++++++++
 packages/core/src/__tests__/no-hardcoded-lifecycle-columns.test.ts |   2 +
 packages/core/src/ai/agent-effective-model.ts      |  82 +++++++++++++++++
 packages/core/src/ai/model-resolution.ts           |  14 +++
 packages/core/src/index.gate.ts                    |   7 ++
 packages/core/src/index.ts                         |   7 ++
 packages/core/src/types.ts                         |   7 ++
 packages/dashboard/app/components/AgentDetailView.tsx |  49 ++++++---
 packages/dashboard/app/components/AgentsView.tsx   |  11 ++-
 packages/dashboard/app/components/__tests__/AgentDetailView.effective-model.test.tsx |  55 +++++++++++
 packages/dashboard/app/components/__tests__/AgentDetailView.settings.test.tsx |  22 +++++
 packages/dashboard/app/components/__tests__/AgentsView.test.tsx |  25 +++++
 packages/dashboard/src/__tests__/chat-manager.test.ts |  45 +++++++++
 packages/dashboard/src/__tests__/routes-chat-sessions-project-model.test.ts | 103 +++++++++++++++++++++
 packages/dashboard/src/chat.ts                     |  65 +++++--------
 packages/dashboard/src/routes/register-chat-routes.ts |  37 +++++---
 packages/engine/src/__tests__/agent-session-helpers-test-mode.test.ts |  13 +++
 packages/engine/src/__tests__/agent-session-helpers.test.ts |  54 +++++++++++
 packages/engine/src/agent-heartbeat.ts             |  10 +-
 packages/engine/src/agents/agent-session-helpers.ts |   6 +-
 23 files changed, 648 insertions(+), 83 deletions(-)

Fusion-Task-Id: FN-8869

Fusion-Task-Lineage: 00227aaf-b33b-43f1-bcbb-2203c9930dbc

Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
2026-08-10 02:25:03 -07:00
gsxdsm
51437558ac fix(workflow): give a stranded planning hold a retry owner, and stop status erasure
FN-8923 sat silent in Todo for 7+ hours with zero run-audit rows. Its plan node
held on principal routing, triage correctly recorded `needs-replan`, and then
dependency auto-unblock nulled that status when its blocker completed. From that
moment the card was invisible to both lanes: triage saw a fully-written spec with
no replan flag and skipped it, while the executor's `isUnplannedForExecution`
refused to dispatch because no capacity-boundary continuation existed. Not stuck
in a retry loop -- unowned.

- Dependency auto-unblock clears only the `queued` marker it owns, at all four
  sites (scheduler.ts plus three in self-healing.ts). `status` is a shared
  lifecycle channel and `needs-replan` is the only signal that re-admits a
  hold-column card whose PROMPT.md is already a real spec.
- New self-healing sweep `reconcilePrincipalHeldPlanningContinuations` re-queues
  planning for a card whose sole active continuation is a principal-routing hold.
  A planning hold otherwise has no retry owner at all. Gated on the planning
  lane, effective auto-merge, an owned (null) status, and the shared planning
  lifecycle lock, so it cannot clobber a triage claim or launder a `failed` /
  `stuck-killed` / `queued` card into a replan.
- Workflow node-instance-id materialization is idempotent across foreach, loop,
  and optional-group containers. It re-wrapped its own output on every dispatch,
  so FN-8869 grew a ~1.8 KB `run_id` of ~30 repeated segments on a hot indexed
  column and every retry read as a distinct run.
- An unresolvable node instance or absent IR now fails closed instead of being
  treated as an edited-away override -- the previous shape would have discarded a
  real reviewer fence and handed a named review to the pool.
- Mirror the routing exports into the gate-safe core barrel; the reduced barrel
  resolved them to `undefined`, a latent trap for any suite reaching the router.

Findings from a multi-reviewer pass; 9 of 11 confirmed by an independent
validator. Each fix carries a regression asserting the invariant across its
surfaces, not the single reported case -- the optional-group accretion test was
verified to fail without the fix.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-10 01:43:54 -07:00
gsxdsm
37d32357e9 fix(workflow): keep assigned tasks executing instead of holding on a dead principal
Workflow principal routing conflated two different questions: whether an agent
CAN run a node, and whether it can run it RIGHT NOW. Both produced a hold, and a
named principal never falls through to the role pool — so an agent that could
never satisfy the node wedged the task permanently.

FN-8869, FN-8928, and FN-8845 were each explicitly assigned to a permanent
engineer-role agent (which the assignment policy allows). Their `step-execute`
nodes took that owner as `task-assignee` authority, found no `executor` tag, and
held closed. Each card re-dispatched and re-held every ~15 minutes for hours
while two idle `Workflow Executor` pool agents were never consulted. The only
thing still touching them was the owner's hourly heartbeat, which logged
"progressing, no blockers" and exited: heartbeat observation had replaced
execution.

- Structural incapability (wrong role, agent deleted, authority edited away) is
  no longer authority for the node. Routing continues to the column binding and
  then the role pool.
- A resumed continuation whose fence proves stale discards it and re-routes,
  instead of re-asserting a dead principal on every dispatch.
- Availability is unchanged and still fail-closed: a role-capable principal that
  is paused, disabled, or at session capacity holds, and is never silently
  replaced by a pool member.
- An explicitly assigned engineer-role agent is now valid task-assignee
  authority for an executor node, so the assigned agent executes its own task
  continuously under graph dispatch. The role pool stays strict, since automatic
  backlog pickup by engineers is a separate opt-in.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-10 00:58:43 -07:00
gsxdsm
a48594f128 FN-8929: dismiss accepted chat attachments
Clear staged chat attachments immediately when the server accepts a direct or room message.

- Add stream and room delivery acceptance callbacks for attachment cleanup.
- Preserve staged files on pre-acceptance failures and block destructive clear commands.
- Cover direct, room, mobile, and stream acceptance behavior with tests.

Files changed:
 .changeset/fn-8929-chat-attachment-dismiss.md      |   7 ++
 docs/dashboard-guide.md                            |   2 +-
 .../app/api/__tests__/legacy-chat-stream.test.ts   |  34 ++++++
 packages/dashboard/app/api/chat/chat.ts            |   7 ++
 packages/dashboard/app/components/ChatView.tsx     |  86 +++++++++------
 .../__tests__/ChatView.core-interactions.test.tsx  | 117 ++++++++++++++++++++-
 .../components/__tests__/ChatView.core.test.tsx    |   6 +-
 .../__tests__/ChatView.mobile-render.test.tsx      |  21 ++++
 .../components/__tests__/ChatView.mobile.test.tsx  |   2 +-
 .../components/__tests__/ChatView.rooms.test.tsx   |  64 ++++++++---
 .../dashboard/app/hooks/__tests__/useChat.test.ts  |  54 ++++++++++
 .../app/hooks/__tests__/useChatRooms.test.ts       |  33 +++++-
 packages/dashboard/app/hooks/useChat.ts            |  11 +-
 packages/dashboard/app/hooks/useChatRooms.ts       |  13 ++-
 14 files changed, 393 insertions(+), 64 deletions(-)

Fusion-Task-Id: FN-8929

Fusion-Task-Lineage: 3f39d1aa-a4f9-4a71-a7a8-3522d7c3c83e

Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
2026-08-09 23:21:02 -07:00
gsxdsm
d9a2d9dba3 FN-8925: clear orphaned paused merge stamps safely
Allow only engine-owned deadlock pauses to clear stale merge stamps without resuming work.

- Fail closed when merge ownership probing is unavailable.
- Clear stale stamps only for merge-deadlock-detected pauses and suppress their enqueue.
- Add race coverage, audit documentation, and a patch changeset.

Files changed:
 .changeset/fn-8925-paused-stale-merge.md           |   7 +
 docs/self-healing-backward-move-audit.md           |   2 +-
 docs/task-management.md                            |   4 +-
 .../self-healing-query-filter-blindness.test.ts    |   5 +-
 .../self-healing-stale-merge-fanout.test.ts        |   1 +
 packages/engine/src/__tests__/self-healing.test.ts | 203 +++++++++++++++++++--
 packages/engine/src/self-healing.ts                |  45 ++++-
 7 files changed, 241 insertions(+), 26 deletions(-)

Fusion-Task-Id: FN-8925

Fusion-Task-Lineage: e5491e1a-ca0a-4a82-b4e0-4002f30e869e

Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>,
2026-08-09 23:13:34 -07:00
gsxdsm
e7a873c505 FN-8936: stabilize Planning Mode handoff tests
Stabilize live Proceed-action handoffs and re-admit the Planning Mode flow suite.

- Settle hydration and re-query the Proceed action before direct-create test clicks.
- Remove the Planning Mode test quarantine and record its rescue in the testing ledger.

Files changed:
 .../suite-only-flakes-observed-register.md           |  4 ++++
 docs/testing.md                                      |  3 +++
 .../PlanningModeModal.planning-flow.test.tsx         | 20 ++++++++++++++++----
 packages/dashboard/vitest.config.ts                  |  5 -----
 scripts/lib/test-quarantine.json                     |  5 -----
 5 files changed, 23 insertions(+), 14 deletions(-)

Fusion-Task-Id: FN-8936

Fusion-Task-Lineage: ed869b67-9394-458b-879c-54da0d7d327e

Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
2026-08-09 23:08:11 -07:00
Phil Larson
b30508c685 fix(executor): preserve external checkout ownership guards (#3404)
## Summary
- keep operator-routed external checkouts out of managed worktree
preflight, cleanup, and lost-work reconciliation paths
- mark injected custom graph worktree creation as native so workspace
mode accepts the managed backend
- add an extraction regression guard for the ownership fences

## Test plan
- `pnpm --filter @fusion/engine exec vitest run
src/executor/__tests__/external-checkout-extraction-guards.test.ts
--silent=passed-only --reporter=dot`
- `pnpm --filter @fusion/engine typecheck`
- `pnpm check:changesets`
- targeted ESLint on the changed TypeScript files


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

* **Bug Fixes**
* External execution checkouts are no longer treated as Fusion-managed
worktrees.
* Prevented unnecessary Git checks, cleanup, and reconciliation during
retries, pauses, recovery, and stuck-task handling.
* Invalid external checkout configurations now fail safely with an
error.
* Graph-injected worktrees now use the native worktree backend for
consistent setup.
* **Tests**
* Added coverage verifying external checkouts remain excluded from
managed worktree operations.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2026-08-09 19:40:01 -10:00
Phil Larson
a5c3476cb2 fix: harden pinned worktree recovery cleanup (#3402)
## Summary
- serialize pinned-path classification, orphan preservation, quarantine
reconciliation, and recreation under one reservation
- preserve cross-filesystem orphans atomically beside the configured
worktree root and retain the newest 10 generated entries per recovery
root
- exclude recovery containers from pool and self-healing scans, with
fail-closed symlink and active-session guards
- document recovery location and retention behavior

## Test plan
- `pnpm --filter @fusion/engine exec vitest run
src/__tests__/worktree-acquisition.test.ts
src/__tests__/worktree-paths.test.ts src/__tests__/worktree-pool.test.ts
src/__tests__/self-healing-tempdir-sweep.test.ts --silent=passed-only
--reporter=dot`
- `pnpm --filter @fusion/engine typecheck`
- `pnpm --filter @fusion/engine build`
- `pnpm test:gate:static`
- `pnpm check:changesets --strict`
- `pnpm check:fnxc-future-dates`


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

* **New Features**
* Preserves orphaned pinned worktrees during recovery, including across
filesystems.
* Retains the 10 most recent recovery entries and safely skips active or
invalid entries.
* Keeps recovery data separate from normal worktree discovery, cleanup,
and capacity checks.
* Adds safeguards for path containment, active-session ownership, and
concurrent recovery.

* **Bug Fixes**
* Prevents pinned worktree data from being lost during recreation or
quarantine cleanup.
* Ensures recovery cleanup failures do not interrupt worktree
acquisition.

* **Documentation**
* Documented orphan recovery, retention, fallback behavior, and cleanup
safeguards.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2026-08-09 19:39:39 -10:00
gsxdsm
a278819d00 FN-8938: fix board-mobile hook import assertion
Correct the mobile board source assertion after the hook relocation.

- Point the test at the hook's current relative import path.

Files changed:
 packages/dashboard/app/components/__tests__/board-mobile.test.tsx | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

Fusion-Task-Id: FN-8938

Fusion-Task-Lineage: a8ae4803-4dfc-481e-aa1e-6fa014592249

Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
2026-08-09 22:28:38 -07:00
gsxdsm
1474c617b5 FN-8918: suppress false parked-task alerts
Prevent stale self-healing observations from notifying operators about active or intentionally held tasks.

- Require ownerless self-healing proof before classifying a task as wedged.
- Revalidate live tasks and suppress notifications for progressing, held, deleted, archived, or complete-lane rows.
- Cover reviewing, typed not-found reads, and archived episode resolution; document the behavior.
- Add a patch changeset for the notification fix.

Files changed:
 .changeset/fn-8918-false-parked-task-alerts.md     |   7 ++
 docs/agents.md                                     |   2 +-
 docs/architecture.md                               |   2 +-
 packages/engine/src/notification/__tests__/task-wedge-notification.test.ts | 121 +++++++++++++++++++--
 packages/engine/src/notification/notification-service.ts | 34 ++++--
 packages/engine/src/notification/task-wedge-notification.ts | 21 +++-
 6 files changed, 166 insertions(+), 21 deletions(-)

Fusion-Task-Id: FN-8918

Fusion-Task-Lineage: 4da385aa-c625-40c5-a781-36becdf94fe5

Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
2026-08-09 21:57:01 -07:00
gsxdsm
a27090217c FN-8930: distinguish task creator from assignee
Make TaskCard provenance visually and semantically distinct from current ownership.

- Render creator provenance with a Sparkles icon and by-name label.
- Style provenance as an outlined chip while preserving the filled owner chip.
- Cover provenance, wrap, and accessible-label behavior with TaskCard tests.

Files changed:
 .changeset/fn-8930-taskcard-provenance-chip.md     |   7 ++
 packages/dashboard/app/components/TaskCard.css     |  17 +++
 packages/dashboard/app/components/TaskCard.tsx     |  12 +-
 .../__tests__/TaskCard.agent-provenance.test.tsx   | 140 +++++++++++++++++++++
 .../__tests__/TaskCard.badge-wrap.test.tsx         |   3 +-
 .../app/components/__tests__/TaskCard.test.tsx     |   4 +-
 6 files changed, 177 insertions(+), 6 deletions(-)

Fusion-Task-Id: FN-8930

Fusion-Task-Lineage: 61fd526d-20be-42f1-b912-94ad9c11e127

Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
2026-08-09 21:50:44 -07:00
gsxdsm
aac090d0c3 FN-8866: add Command Center agent activity timeline
Add a Command Center live agent activity log with historical navigation.

- Stream and paginate organization-wide agent activity with timeline filters.
- Link activity rows to agent details and task views while reusing feed presentation.
- Add localized copy, documentation, a minor changeset, and coverage for live and scroll-back behavior.

Files changed:
 .changeset/fn-8866-agent-activity-surfaces.md      |   7 +
 docs/dashboard-guide.md                            |   1 +
 packages/dashboard/app/App.tsx                     |  12 ++
 packages/dashboard/app/components/ActivityFeed.tsx |  77 ++++----
 packages/dashboard/app/components/AgentsView.tsx   |  13 +-
 .../__tests__/AgentsView.focus-agent.test.tsx      |  59 ++++++
 .../command-center/AgentActivityPanel.css          |  98 +++++++++
 .../command-center/AgentActivityPanel.tsx          |  99 ++++++++++
 .../components/command-center/CommandCenter.tsx    |   9 +
 .../__tests__/AgentActivityPanel.mobile.test.ts    |  15 ++
 .../AgentActivityPanel.sse-integration.test.tsx    |  96 +++++++++
 .../__tests__/AgentActivityPanel.test.tsx          | 121 ++++++++++++
 .../__tests__/CommandCenter.test.tsx               |  27 ++-
 .../__tests__/agentActivityCursor.test.ts          |  47 +++++
 .../__tests__/useAgentActivity.test.tsx            |  14 ++
 .../command-center/agentActivityCursor.ts          |  75 +++++++
 .../command-center/agentActivityPresentation.tsx   |   8 +
 .../components/command-center/useAgentActivity.ts  | 218 +++++++++++++++++++++
 .../app/components/dashboard/MainContent.tsx       |  19 ++
 .../MainContent.agent-activity-navigation.test.tsx | 127 ++++++++++++
 .../dashboard/app/components/dashboard/types.ts    |   3 +
 packages/i18n/locales/en/app.json                  |  30 ++-
 packages/i18n/locales/es/app.json                  |  30 ++-
 packages/i18n/locales/fr/app.json                  |  30 ++-
 packages/i18n/locales/ko/app.json                  |  30 ++-
 packages/i18n/locales/pt-BR/app.json               |  30 ++-
 packages/i18n/locales/zh-CN/app.json               |  30 ++-
 packages/i18n/locales/zh-TW/app.json               |  30 ++-
 28 files changed, 1312 insertions(+), 43 deletions(-)

Fusion-Task-Id: FN-8866

Fusion-Task-Lineage: 817ae76e-52c6-4221-8642-df757e773627

Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
2026-08-09 20:19:52 -07:00
gsxdsm
f5a9cf01b1 FN-8912: recover stale merge status after aborts
Clear orphaned transient merge statuses so bounded retries can proceed.

- Fence aborted merge generations from writing transient task statuses.
- Reconcile and clear unconfirmed merge stamps in the queue and stale-state recovery paths.
- Cover timeout, abort, retry, and self-healing behavior with regression tests.

Files changed:
 .changeset/fn-8912-merge-abort-transient-status.md |   7 +
 docs/architecture.md                               |   1 +
 docs/task-management.md                            |   2 +-
 .../merge-abort-clears-transient-status.test.ts    | 410 +++++++++++++++++++++
 .../src/__tests__/merge-active-status.test.ts      |  21 ++
 packages/engine/src/__tests__/self-healing.test.ts | 231 ++++++++++--
 packages/engine/src/merge/merge-active-status.ts   |  18 +
 packages/engine/src/merge/merger-ai.ts             |  34 +-
 packages/engine/src/project-engine.ts              |  49 ++-
 packages/engine/src/self-healing.ts                |  72 +++-
 10 files changed, 810 insertions(+), 35 deletions(-)

Fusion-Task-Id: FN-8912

Fusion-Task-Lineage: 8b2b7506-ed3e-41d4-9e54-59f45abc0020

Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
2026-08-09 20:14:10 -07:00
gsxdsm
351c3b7e28 fix(executor): restore two fixes dropped by the U4 executor peel
PR #3317 rewrote executor.ts from a pre-change base. A refactor rebased off a
stale base does not conflict — it deletes. An audit of every commit touching
executor.ts before the peel found two landed fixes silently removed:

- FN-8850 completion recommendations: the `fn_task_done` VALIDATOR survived the
  peel but the engine-appended prompt section asking the executor to produce
  recommendations did not. Nothing failed — recommendations were still accepted,
  no test covered the prompt wiring, and capture simply stopped. Restored
  verbatim in executor/system-prompt.ts, next to the validator it pairs with.
- The WorktreeBaseRefreshError guard (a06a4988d9): without it a pre-session
  checkout refusal falls through to the generic terminal sink and parks the task
  `failed`, which is the path that produced 99 parks and 47 operator alerts over
  2026-08-01..09. Restored in executor/run-implementation.ts as a wait.

Everything else from that window verified intact: external checkout routing
(#3398/#3400/#3401), FN-8864/FN-8868 agent activity telemetry, FN-8863
remediation holds, FN-8841 CLOSE_NO_OP, FN-8870 approval mail, FN-8910 held-task
remediation — line-level diffs looked missing only because the peel rewrote
`this.` to `deps.`.

Adds executor-prompt-completion-recommendations.test.ts to pin the prompt
wiring, since the absence of coverage is what let it disappear silently.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-09 19:10:45 -07:00
gsxdsm
51a5e1c275 fix(agents): separate heartbeat runtime from workflow routability
runtimeConfig.enabled answered two different questions. Every consumer in the
engine reads it as "run this agent's own durable heartbeat loop" — heartbeat
scheduling, error recovery, self-healing, in-process runtime — except the
workflow router, which also read it as "may own a workflow stage".

That conflation caused both failures:

- Built-in owners ship with the heartbeat off, which is CORRECT (they are
  invoked by the workflow engine and must not run autonomous loops or auto-claim
  work). That silently made every built-in role unroutable and deadlocked the
  board.
- Enabling the heartbeat to restore routing then gave four agents autonomous
  loops and auto-claiming nobody asked for.

Separate the flag:

- runtimeConfig.enabled governs the heartbeat runtime ONLY
- isWorkflowPrincipalEligible answers routability, and treats the four built-in
  role owners as routable structurally — there is no fallback if a role cannot
  route, so "unroutable" is not a state an operator can meaningfully select
- provision built-ins { enabled: false, autoClaimRelevantTasks: false }
- paused/errored still outranks the exemption, so it can never resurrect a
  broken principal

Removes the earlier write-seam coercion that forced enabled:true — the invariant
is now structural rather than fought for on every write.

Also re-applies the principal-hold backoff ladder (15s -> 5m, checked before
graph entry) into executor/execute-workflow-graph.ts. PR #3317's executor peel
rewrote executor.ts from a pre-change base and dropped it.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-09 19:03:23 -07:00
gsxdsm
1cf86baa1c refactor: package code organization wave 18 (executor pure peels) (#3317)
## Summary

Wave 18 continues the package code-organization program after wave 17
domain folders (U4 Slice A from
`docs/plans/2026-07-14-001-refactor-package-code-organization-plan.md`).

### What changed
Peel **pure, behavior-preserving** helpers out of
`packages/engine/src/executor.ts` into domain modules under
`packages/engine/src/executor/`, with **stable re-exports** from
`executor.ts` so deep imports and `vi.mock("../executor.js")` keep
working.

| New module | Symbols |
|------------|---------|
| `executor/task-done-refusal.ts` | `evaluateTaskDoneRefusal`,
`determineRevisionResetStart`, skip-bypass refusal helper |
| `executor/workflow-feedback-paths.ts` |
`extractReferencedPathsFromWorkflowFeedback`,
`isAlwaysAllowedScopeLeakPath`, `workflowPathMatchesDeclaredScope` |
| `executor/workflow-step-verdict.ts` |
`FUSION_WORKFLOW_STEP_CONVENTIONS_PREAMBLE`, `parseWorkflowStepVerdict`
/ `parseWorkflowStepOutput`, step outcome types |
| `executor/await-input-parse.ts` | `parseAwaitInputSentinel`,
`parseAwaitInputQuestionToolCall` |
| `executor/no-commit-eligibility.ts` | `getNoCommitEligibilityReason`
(+ prompt heuristics) |

`executor.ts` live LOC ~**22817 → ~22427** (first pure-peel batch; more
peels needed to approach the 2k cap).

### Shims
- `old path` `executor.ts` public exports → `new path` `executor/*.ts` →
delete-when consumer deep-imports are re-pointed (not this PR)

### Test plan
- [x] `@fusion/engine` typecheck
- [x] Oracle: task-done refusal, skip-bypass, workflow malformed
verdict, scope-leak allowlist, executor-step-session, executor-prompt
- [x] `vitest --project=engine-core` (merge-gate curated suite)
- [ ] CI merge gate

**Stack:** wave17 (merged) → **this PR**

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

* **New Features**
* Improved recognition of workflow outcomes from structured and
conversational responses.
* Added support for extracting questions from await-input responses and
tool calls.
* Improved workflow feedback handling for referenced files and declared
scope patterns.
* Added clearer guidance for task execution, approvals, verification,
and available tools.

* **Bug Fixes**
* Prevented completion when required review approvals are missing or
revisions remain pending.
* Improved handling of workflows that legitimately require no code
changes.
  * Added clearer refusal messages and more reliable revision restarts.
  * Sanitized repository paths in Git remediation instructions.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2026-08-09 15:46:09 -10:00
dependabot[bot]
746a8f0808 Bump electron-builder from 26.8.1 to 26.15.3 (#3372)
Bumps
[electron-builder](https://github.com/electron-userland/electron-builder/tree/HEAD/packages/electron-builder)
from 26.8.1 to 26.15.3.
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/electron-userland/electron-builder/releases">electron-builder's
releases</a>.</em></p>
<blockquote>
<h2>electron-builder@26.15.3</h2>
<h2>What's Changed</h2>
<ul>
<li>fix: add <code>ELECTRON_BUILDER_BINARIES_ALLOW_HTTP</code> opt-in
for parsing env vars targeting non-localhost HTTP by <a
href="https://github.com/mmaietta"><code>@​mmaietta</code></a> in <a
href="https://redirect.github.com/electron-userland/electron-builder/pull/9856">electron-userland/electron-builder#9856</a></li>
<li>docs: update docs and add build lifecycle page by <a
href="https://github.com/mmaietta"><code>@​mmaietta</code></a> in <a
href="https://redirect.github.com/electron-userland/electron-builder/pull/9857">electron-userland/electron-builder#9857</a></li>
<li>chore(test): update vitest by <a
href="https://github.com/mmaietta"><code>@​mmaietta</code></a> in <a
href="https://redirect.github.com/electron-userland/electron-builder/pull/9859">electron-userland/electron-builder#9859</a></li>
<li>fix(download): persist toolset archives in a predictable cache path
by <a href="https://github.com/mmaietta"><code>@​mmaietta</code></a> in
<a
href="https://redirect.github.com/electron-userland/electron-builder/pull/9861">electron-userland/electron-builder#9861</a></li>
<li>fix(publish): set <code>x-amz-content-sha256:
UNSIGNED-PAYLOAD</code> before SigV4 signing by <a
href="https://github.com/mmaietta"><code>@​mmaietta</code></a> in <a
href="https://redirect.github.com/electron-userland/electron-builder/pull/9863">electron-userland/electron-builder#9863</a></li>
<li>chore(deploy): Release (next) by <a
href="https://github.com/electron-builder-release-bot"><code>@​electron-builder-release-bot</code></a>[bot]
in <a
href="https://redirect.github.com/electron-userland/electron-builder/pull/9858">electron-userland/electron-builder#9858</a></li>
</ul>
<p><strong>Full Changelog</strong>: <a
href="https://github.com/electron-userland/electron-builder/compare/electron-builder@26.15.2...electron-builder@26.15.3">https://github.com/electron-userland/electron-builder/compare/electron-builder@26.15.2...electron-builder@26.15.3</a></p>
<h2>electron-builder@26.15.2</h2>
<h2>What's Changed</h2>
<ul>
<li>fix(mac): use native <code>zip</code> for macOS zip target to
preserve <code>.framework</code> symlinks by <a
href="https://github.com/mmaietta"><code>@​mmaietta</code></a> in <a
href="https://redirect.github.com/electron-userland/electron-builder/pull/9847">electron-userland/electron-builder#9847</a></li>
<li>fix: retry package install on transient network errors by <a
href="https://github.com/mmaietta"><code>@​mmaietta</code></a> in <a
href="https://redirect.github.com/electron-userland/electron-builder/pull/9850">electron-userland/electron-builder#9850</a></li>
<li>fix(icons): replace png2icons with wasm-vips Lanczos3 resampling for
high-quality icon conversion by <a
href="https://github.com/mmaietta"><code>@​mmaietta</code></a> in <a
href="https://redirect.github.com/electron-userland/electron-builder/pull/9851">electron-userland/electron-builder#9851</a></li>
<li>chore(deploy): Release (next) by <a
href="https://github.com/electron-builder-release-bot"><code>@​electron-builder-release-bot</code></a>[bot]
in <a
href="https://redirect.github.com/electron-userland/electron-builder/pull/9848">electron-userland/electron-builder#9848</a></li>
</ul>
<p><strong>Full Changelog</strong>: <a
href="https://github.com/electron-userland/electron-builder/compare/electron-builder@26.15.1...electron-builder@26.15.2">https://github.com/electron-userland/electron-builder/compare/electron-builder@26.15.1...electron-builder@26.15.2</a></p>
<h2>electron-builder@26.15.1</h2>
<h2>What's Changed</h2>
<ul>
<li>fix: validate packages by <a
href="https://github.com/regnete"><code>@​regnete</code></a> in <a
href="https://redirect.github.com/electron-userland/electron-builder/pull/9838">electron-userland/electron-builder#9838</a></li>
<li>chore: <code>ci:validate</code>, <code>ci:test:fetch-cache</code>
simplification, and code cleanup by <a
href="https://github.com/mmaietta"><code>@​mmaietta</code></a> in <a
href="https://redirect.github.com/electron-userland/electron-builder/pull/9840">electron-userland/electron-builder#9840</a></li>
<li>chore(tests): embed platform markers in generated test filenames to
eliminate &quot;unknown&quot; timing in shard plan by <a
href="https://github.com/mmaietta"><code>@​mmaietta</code></a> in <a
href="https://redirect.github.com/electron-userland/electron-builder/pull/9843">electron-userland/electron-builder#9843</a></li>
<li>chore(deps): update and unpin <code>@​electron/rebuild</code>
package by <a
href="https://github.com/mmaietta"><code>@​mmaietta</code></a> in <a
href="https://redirect.github.com/electron-userland/electron-builder/pull/9845">electron-userland/electron-builder#9845</a></li>
<li>chore(deploy): Release (next) by <a
href="https://github.com/electron-builder-release-bot"><code>@​electron-builder-release-bot</code></a>[bot]
in <a
href="https://redirect.github.com/electron-userland/electron-builder/pull/9842">electron-userland/electron-builder#9842</a></li>
</ul>
<p><strong>Full Changelog</strong>: <a
href="https://github.com/electron-userland/electron-builder/compare/electron-builder@26.15.0...electron-builder@26.15.1">https://github.com/electron-userland/electron-builder/compare/electron-builder@26.15.0...electron-builder@26.15.1</a></p>
<h2>electron-builder@26.15.0</h2>
<h2>What's Changed</h2>
<ul>
<li>chore(pr-labeler): force run to only pull_request on core repo
(ignore forks) by <a
href="https://github.com/mmaietta"><code>@​mmaietta</code></a> in <a
href="https://redirect.github.com/electron-userland/electron-builder/pull/9821">electron-userland/electron-builder#9821</a></li>
<li>fix(mac): skip signing when no certificate found; warn on ad-hoc +
hardenedRuntime by <a
href="https://github.com/mmaietta"><code>@​mmaietta</code></a> in <a
href="https://redirect.github.com/electron-userland/electron-builder/pull/9822">electron-userland/electron-builder#9822</a></li>
<li>fix: use <code>bash</code> instead of <code>sh</code> in
package.json by <a
href="https://github.com/mmaietta"><code>@​mmaietta</code></a> in <a
href="https://redirect.github.com/electron-userland/electron-builder/pull/9823">electron-userland/electron-builder#9823</a></li>
<li>chore: replace app-builder-bin <code>node-dep-tree</code> and
<code>rebuild-node-modules</code> by <a
href="https://github.com/mmaietta"><code>@​mmaietta</code></a> in <a
href="https://redirect.github.com/electron-userland/electron-builder/pull/9828">electron-userland/electron-builder#9828</a></li>
<li>feat(node-module-collector): invoke package managers via PowerShell
<code>-EncodedCommand</code> on Windows by <a
href="https://github.com/mmaietta"><code>@​mmaietta</code></a> in <a
href="https://redirect.github.com/electron-userland/electron-builder/pull/9827">electron-userland/electron-builder#9827</a></li>
<li>feat(migration): fully replace Go binary
<code>app-builder-bin</code> with TS implementation by <a
href="https://github.com/mmaietta"><code>@​mmaietta</code></a> in <a
href="https://redirect.github.com/electron-userland/electron-builder/pull/9829">electron-userland/electron-builder#9829</a></li>
<li>chore(test): print out shard duration calculation results during
<code>shard-count</code> step by <a
href="https://github.com/mmaietta"><code>@​mmaietta</code></a> in <a
href="https://redirect.github.com/electron-userland/electron-builder/pull/9831">electron-userland/electron-builder#9831</a></li>
<li>feat(linux): add <code>syncDesktopName</code> option to align
installed <code>.desktop</code> filename with
<code>StartupWMClass</code> by <a
href="https://github.com/mmaietta"><code>@​mmaietta</code></a> in <a
href="https://redirect.github.com/electron-userland/electron-builder/pull/9832">electron-userland/electron-builder#9832</a></li>
<li>fix(smart-cache): deep-merge shard caches so linux timing data
survives across runs by <a
href="https://github.com/mmaietta"><code>@​mmaietta</code></a> in <a
href="https://redirect.github.com/electron-userland/electron-builder/pull/9833">electron-userland/electron-builder#9833</a></li>
<li>fix: holistic field detection for <code>&lt;text&gt; (sha256
hash)</code> redaction by <a
href="https://github.com/mmaietta"><code>@​mmaietta</code></a> in <a
href="https://redirect.github.com/electron-userland/electron-builder/pull/9834">electron-userland/electron-builder#9834</a></li>
<li>fix(mac/pkg): explicitly pass <code>--version</code> to pkgbuild by
<a href="https://github.com/mmaietta"><code>@​mmaietta</code></a> in <a
href="https://redirect.github.com/electron-userland/electron-builder/pull/9835">electron-userland/electron-builder#9835</a></li>
<li>chore(deploy): Release (next) by <a
href="https://github.com/electron-builder-release-bot"><code>@​electron-builder-release-bot</code></a>[bot]
in <a
href="https://redirect.github.com/electron-userland/electron-builder/pull/9825">electron-userland/electron-builder#9825</a></li>
</ul>
<p><strong>Full Changelog</strong>: <a
href="https://github.com/electron-userland/electron-builder/compare/electron-builder@26.14.0...electron-builder@26.15.0">https://github.com/electron-userland/electron-builder/compare/electron-builder@26.14.0...electron-builder@26.15.0</a></p>
<!-- raw HTML omitted -->
</blockquote>
<p>... (truncated)</p>
</details>
<details>
<summary>Changelog</summary>
<p><em>Sourced from <a
href="https://github.com/electron-userland/electron-builder/blob/master/packages/electron-builder/CHANGELOG.md">electron-builder's
changelog</a>.</em></p>
<blockquote>
<h2>26.15.3</h2>
<h3>Patch Changes</h3>
<!-- raw HTML omitted -->
<!-- raw HTML omitted -->
<p><a
href="35bfdebe47"><code>35bfdeb</code></a>
<a
href="9097daff78"><code>9097daf</code></a></p>
<!-- raw HTML omitted -->
<ul>
<li><code>builder-util@26.15.3</code></li>
<li><code>app-builder-lib@26.15.3</code></li>
<li><code>dmg-builder@26.15.3</code></li>
</ul>
<!-- raw HTML omitted -->
<h2>26.15.2</h2>
<h3>Patch Changes</h3>
<!-- raw HTML omitted -->
<!-- raw HTML omitted -->
<p><a
href="e25f9674d5"><code>e25f967</code></a>
<a
href="f5ae1632a7"><code>f5ae163</code></a>
<a
href="3951024641"><code>3951024</code></a></p>
<!-- raw HTML omitted -->
<ul>
<li><code>app-builder-lib@26.15.2</code></li>
<li><code>dmg-builder@26.15.2</code></li>
</ul>
<!-- raw HTML omitted -->
<h2>26.15.1</h2>
<h3>Patch Changes</h3>
<!-- raw HTML omitted -->
<!-- raw HTML omitted -->
<p><a
href="588ff4854e"><code>588ff48</code></a>
<a
href="db5d2f4bfe"><code>db5d2f4</code></a></p>
<!-- raw HTML omitted -->
<ul>
<li><code>app-builder-lib@26.15.1</code></li>
<li><code>dmg-builder@26.15.1</code></li>
</ul>
<!-- raw HTML omitted -->
</blockquote>
<p>... (truncated)</p>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="512a57ec9b"><code>512a57e</code></a>
chore(deploy): Release v26.15.3 (<a
href="https://github.com/electron-userland/electron-builder/tree/HEAD/packages/electron-builder/issues/9858">#9858</a>)</li>
<li><a
href="a6117b3011"><code>a6117b3</code></a>
chore(deploy): Release v26.15.2 (<a
href="https://github.com/electron-userland/electron-builder/tree/HEAD/packages/electron-builder/issues/9848">#9848</a>)</li>
<li><a
href="d57f094172"><code>d57f094</code></a>
chore(deploy): Release v26.15.1 (<a
href="https://github.com/electron-userland/electron-builder/tree/HEAD/packages/electron-builder/issues/9842">#9842</a>)</li>
<li><a
href="bed3a9c421"><code>bed3a9c</code></a>
chore(deploy): Release v26.15.0 (electron-updater@6.8.9) (<a
href="https://github.com/electron-userland/electron-builder/tree/HEAD/packages/electron-builder/issues/9825">#9825</a>)</li>
<li><a
href="198c10c969"><code>198c10c</code></a>
chore: replace app-builder-bin <code>node-dep-tree</code> and
<code>rebuild-node-modules</code> (<a
href="https://github.com/electron-userland/electron-builder/tree/HEAD/packages/electron-builder/issues/9">#9</a>...</li>
<li><a
href="e236392bd9"><code>e236392</code></a>
chore(deploy): Release v26.14.0 (<a
href="https://github.com/electron-userland/electron-builder/tree/HEAD/packages/electron-builder/issues/9812">#9812</a>)</li>
<li><a
href="72d298c438"><code>72d298c</code></a>
chore(deploy): Release v26.13.1 (<a
href="https://github.com/electron-userland/electron-builder/tree/HEAD/packages/electron-builder/issues/9805">#9805</a>)</li>
<li><a
href="ffd11c7988"><code>ffd11c7</code></a>
chore(deploy): Release v26.13.0 (electron-updater@6.8.8) (<a
href="https://github.com/electron-userland/electron-builder/tree/HEAD/packages/electron-builder/issues/9793">#9793</a>)</li>
<li><a
href="2c8c71a68a"><code>2c8c71a</code></a>
chore(deploy): Release v26.12.1 (electron-updater@6.8.7) (<a
href="https://github.com/electron-userland/electron-builder/tree/HEAD/packages/electron-builder/issues/9782">#9782</a>)</li>
<li><a
href="d6a5aee821"><code>d6a5aee</code></a>
fix: harden generated-file output, argument construction, and download
valida...</li>
<li>Additional commits viewable in <a
href="https://github.com/electron-userland/electron-builder/commits/electron-builder@26.15.3/packages/electron-builder">compare
view</a></li>
</ul>
</details>
<br />

Co-authored-by: gsxdsm <gsxdsm@users.noreply.github.com>
2026-08-09 15:36:13 -10:00
dependabot[bot]
800a68f58a Bump @codemirror/language from 6.12.3 to 6.12.4 (#3371)
Bumps [@codemirror/language](https://github.com/codemirror/language)
from 6.12.3 to 6.12.4.
<details>
<summary>Commits</summary>
<ul>
<li>See full diff in <a
href="https://github.com/codemirror/language/commits">compare
view</a></li>
</ul>
</details>
<br />

Co-authored-by: gsxdsm <gsxdsm@users.noreply.github.com>
2026-08-09 15:35:58 -10:00
dependabot[bot]
ca224c8804 Bump @xterm/addon-unicode11 from 0.8.0 to 0.9.0 (#3369)
Bumps [@xterm/addon-unicode11](https://github.com/xtermjs/xterm.js) from
0.8.0 to 0.9.0.
<details>
<summary>Commits</summary>
<ul>
<li><a
href="0b603952c3"><code>0b60395</code></a>
Removed debugging stuff</li>
<li>See full diff in <a
href="https://github.com/xtermjs/xterm.js/compare/0.8...0.9">compare
view</a></li>
</ul>
</details>
<br />

Co-authored-by: gsxdsm <gsxdsm@users.noreply.github.com>
2026-08-09 15:35:26 -10:00
dependabot[bot]
71f77e7ac5 Bump typescript-eslint from 8.58.1 to 8.66.0 (#3368)
Bumps
[typescript-eslint](https://github.com/typescript-eslint/typescript-eslint/tree/HEAD/packages/typescript-eslint)
from 8.58.1 to 8.66.0.
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/typescript-eslint/typescript-eslint/releases">typescript-eslint's
releases</a>.</em></p>
<blockquote>
<h2>v8.66.0</h2>
<h2>8.66.0 (2026-08-03)</h2>
<h3>🚀 Features</h3>
<ul>
<li><strong>typescript-estree:</strong> handle import.defer() as
ImportExpression (<a
href="https://redirect.github.com/typescript-eslint/typescript-eslint/pull/12609">#12609</a>)</li>
</ul>
<h3>🩹 Fixes</h3>
<ul>
<li><strong>eslint-plugin:</strong> [no-unnecessary-type-conversion]
ignore shadowed built-ins (<a
href="https://redirect.github.com/typescript-eslint/typescript-eslint/pull/12590">#12590</a>)</li>
<li><strong>eslint-plugin:</strong> [prefer-nullish-coalescing] handle
shadowed Boolean calls (<a
href="https://redirect.github.com/typescript-eslint/typescript-eslint/pull/12591">#12591</a>)</li>
<li><strong>eslint-plugin:</strong> [no-useless-default-assignment]
don't report defaults used by other overloads (<a
href="https://redirect.github.com/typescript-eslint/typescript-eslint/pull/12607">#12607</a>)</li>
<li><strong>eslint-plugin:</strong> [no-unnecessary-type-parameters]
check MappedType key remapping (<a
href="https://redirect.github.com/typescript-eslint/typescript-eslint/pull/12588">#12588</a>)</li>
<li><strong>eslint-plugin:</strong> [class-literal-property-style]
preserve type annotations and don't drop decorators (<a
href="https://redirect.github.com/typescript-eslint/typescript-eslint/pull/12617">#12617</a>)</li>
<li><strong>website:</strong> list
<code>onUnsupportedTypeScriptVersion</code> in parser options (<a
href="https://redirect.github.com/typescript-eslint/typescript-eslint/pull/12585">#12585</a>)</li>
</ul>
<h3>❤️ Thank You</h3>
<ul>
<li>Evyatar Daud <a
href="https://github.com/StyleShit"><code>@​StyleShit</code></a></li>
<li>Kirk Waiblinger <a
href="https://github.com/kirkwaiblinger"><code>@​kirkwaiblinger</code></a></li>
<li>Steffen Schroeder</li>
<li>송재욱</li>
</ul>
<p>See <a
href="https://github.com/typescript-eslint/typescript-eslint/releases/tag/v8.66.0">GitHub
Releases</a> for more information.</p>
<p>You can read about our <a
href="https://typescript-eslint.io/users/versioning">versioning
strategy</a> and <a
href="https://typescript-eslint.io/users/releases">releases</a> on our
website.</p>
<h2>v8.65.0</h2>
<h2>8.65.0 (2026-07-20)</h2>
<h3>🚀 Features</h3>
<ul>
<li>add warning when TS 7 is detected (<a
href="https://redirect.github.com/typescript-eslint/typescript-eslint/pull/12529">#12529</a>)</li>
<li><strong>eslint-plugin:</strong> [no-restricted-imports] deprecate
extension rule (<a
href="https://redirect.github.com/typescript-eslint/typescript-eslint/pull/12527">#12527</a>,
<a
href="https://redirect.github.com/typescript-eslint/typescript-eslint/issues/19562">#19562</a>,
<a
href="https://redirect.github.com/typescript-eslint/typescript-eslint/issues/11889">#11889</a>)</li>
<li><strong>eslint-plugin:</strong> [no-shadow] specialized error on
enum declaration and member shadowing (<a
href="https://redirect.github.com/typescript-eslint/typescript-eslint/pull/12578">#12578</a>)</li>
<li><strong>parser:</strong> add onUnsupportedTypeScriptVersion option
to error on unsupported TypeScript versions (<a
href="https://redirect.github.com/typescript-eslint/typescript-eslint/pull/12465">#12465</a>)</li>
<li><strong>typescript-estree:</strong> throw for invalid import defer
syntax (<a
href="https://redirect.github.com/typescript-eslint/typescript-eslint/pull/12552">#12552</a>)</li>
</ul>
<h3>🩹 Fixes</h3>
<ul>
<li><strong>eslint-plugin:</strong> [prefer-string-starts-ends-with]
handle escaped $ ending regex literals (<a
href="https://redirect.github.com/typescript-eslint/typescript-eslint/pull/12515">#12515</a>)</li>
<li><strong>eslint-plugin:</strong> [unbound-method] report unbound
methods accessed via member expression on union types (<a
href="https://redirect.github.com/typescript-eslint/typescript-eslint/pull/12448">#12448</a>)</li>
<li><strong>eslint-plugin:</strong>
[no-unnecessary-parameter-property-assignment] don't flag computed
assignments with a variable key (<a
href="https://redirect.github.com/typescript-eslint/typescript-eslint/pull/12568">#12568</a>)</li>
</ul>
<h3>❤️ Thank You</h3>
<ul>
<li>Evyatar Daud <a
href="https://github.com/StyleShit"><code>@​StyleShit</code></a></li>
<li>Josh Goldberg ✨</li>
<li>Kirk Waiblinger <a
href="https://github.com/kirkwaiblinger"><code>@​kirkwaiblinger</code></a></li>
<li>Serhii Leniv <a
href="https://github.com/Serhii-Leniv"><code>@​Serhii-Leniv</code></a></li>
</ul>
<!-- raw HTML omitted -->
</blockquote>
<p>... (truncated)</p>
</details>
<details>
<summary>Changelog</summary>
<p><em>Sourced from <a
href="https://github.com/typescript-eslint/typescript-eslint/blob/main/packages/typescript-eslint/CHANGELOG.md">typescript-eslint's
changelog</a>.</em></p>
<blockquote>
<h2>8.66.0 (2026-08-03)</h2>
<p>This was a version bump only for typescript-eslint to align it with
other projects, there were no code changes.</p>
<p>See <a
href="https://github.com/typescript-eslint/typescript-eslint/releases/tag/v8.66.0">GitHub
Releases</a> for more information.</p>
<p>You can read about our <a
href="https://typescript-eslint.io/users/versioning">versioning
strategy</a> and <a
href="https://typescript-eslint.io/users/releases">releases</a> on our
website.</p>
<h2>8.65.0 (2026-07-20)</h2>
<h3>🚀 Features</h3>
<ul>
<li>add warning when TS 7 is detected (<a
href="https://redirect.github.com/typescript-eslint/typescript-eslint/pull/12529">#12529</a>)</li>
</ul>
<h3>❤️ Thank You</h3>
<ul>
<li>Josh Goldberg ✨</li>
<li>Kirk Waiblinger <a
href="https://github.com/kirkwaiblinger"><code>@​kirkwaiblinger</code></a></li>
</ul>
<p>See <a
href="https://github.com/typescript-eslint/typescript-eslint/releases/tag/v8.65.0">GitHub
Releases</a> for more information.</p>
<p>You can read about our <a
href="https://typescript-eslint.io/users/versioning">versioning
strategy</a> and <a
href="https://typescript-eslint.io/users/releases">releases</a> on our
website.</p>
<h2>8.64.0 (2026-07-13)</h2>
<p>This was a version bump only for typescript-eslint to align it with
other projects, there were no code changes.</p>
<p>See <a
href="https://github.com/typescript-eslint/typescript-eslint/releases/tag/v8.64.0">GitHub
Releases</a> for more information.</p>
<p>You can read about our <a
href="https://typescript-eslint.io/users/versioning">versioning
strategy</a> and <a
href="https://typescript-eslint.io/users/releases">releases</a> on our
website.</p>
<h2>8.63.0 (2026-07-06)</h2>
<p>This was a version bump only for typescript-eslint to align it with
other projects, there were no code changes.</p>
<p>See <a
href="https://github.com/typescript-eslint/typescript-eslint/releases/tag/v8.63.0">GitHub
Releases</a> for more information.</p>
<p>You can read about our <a
href="https://typescript-eslint.io/users/versioning">versioning
strategy</a> and <a
href="https://typescript-eslint.io/users/releases">releases</a> on our
website.</p>
<h2>8.62.1 (2026-06-29)</h2>
<p>This was a version bump only for typescript-eslint to align it with
other projects, there were no code changes.</p>
<p>See <a
href="https://github.com/typescript-eslint/typescript-eslint/releases/tag/v8.62.1">GitHub
Releases</a> for more information.</p>
<p>You can read about our <a
href="https://typescript-eslint.io/users/versioning">versioning
strategy</a> and <a
href="https://typescript-eslint.io/users/releases">releases</a> on our
website.</p>
<h2>8.62.0 (2026-06-22)</h2>
<h3>🚀 Features</h3>
<!-- raw HTML omitted -->
</blockquote>
<p>... (truncated)</p>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="e51b11ba3a"><code>e51b11b</code></a>
chore(release): publish 8.66.0</li>
<li><a
href="63ba81b6ac"><code>63ba81b</code></a>
chore(release): publish 8.65.0</li>
<li><a
href="eaf457631a"><code>eaf4576</code></a>
feat: add warning when TS 7 is detected (<a
href="https://github.com/typescript-eslint/typescript-eslint/tree/HEAD/packages/typescript-eslint/issues/12529">#12529</a>)</li>
<li><a
href="0d06406f31"><code>0d06406</code></a>
chore: add attw validation to repo (<a
href="https://github.com/typescript-eslint/typescript-eslint/tree/HEAD/packages/typescript-eslint/issues/12437">#12437</a>)</li>
<li><a
href="c2386e49ec"><code>c2386e4</code></a>
chore(deps): update dependency prettier to v3.9.5 (<a
href="https://github.com/typescript-eslint/typescript-eslint/tree/HEAD/packages/typescript-eslint/issues/12486">#12486</a>)</li>
<li><a
href="414d9abbf6"><code>414d9ab</code></a>
chore(release): publish 8.64.0</li>
<li><a
href="290cf6cdcc"><code>290cf6c</code></a>
chore(release): publish 8.63.0</li>
<li><a
href="3ea32f4ba5"><code>3ea32f4</code></a>
chore(release): publish 8.62.1</li>
<li><a
href="54e285728e"><code>54e2857</code></a>
chore(release): publish 8.62.0</li>
<li><a
href="81e4c2654c"><code>81e4c26</code></a>
feat: remove redundant package.json &quot;files&quot; (<a
href="https://github.com/typescript-eslint/typescript-eslint/tree/HEAD/packages/typescript-eslint/issues/12444">#12444</a>)</li>
<li>Additional commits viewable in <a
href="https://github.com/typescript-eslint/typescript-eslint/commits/v8.66.0/packages/typescript-eslint">compare
view</a></li>
</ul>
</details>
<br />

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-08-09 15:33:51 -10:00
dependabot[bot]
763003c9b0 chore(deps): bump actions/setup-java from 5.6.0 to 5.7.0 (#3367)
Bumps [actions/setup-java](https://github.com/actions/setup-java) from
5.6.0 to 5.7.0.
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/actions/setup-java/releases">actions/setup-java's
releases</a>.</em></p>
<blockquote>
<h2>v5.7.0</h2>
<h2>What's Changed</h2>
<ul>
<li>Fix npm audit failures on releases/v5 by <a
href="https://github.com/brunoborges"><code>@​brunoborges</code></a> in
<a
href="https://redirect.github.com/actions/setup-java/pull/1154">actions/setup-java#1154</a></li>
<li>Backport <a
href="https://redirect.github.com/actions/setup-java/issues/1151">#1151</a>:
Fix missing wrapper cache distributions by <a
href="https://github.com/brunoborges"><code>@​brunoborges</code></a> in
<a
href="https://redirect.github.com/actions/setup-java/pull/1153">actions/setup-java#1153</a></li>
<li>Deprecate legacy Adopt distributions in v5 by <a
href="https://github.com/brunoborges"><code>@​brunoborges</code></a> in
<a
href="https://redirect.github.com/actions/setup-java/pull/1186">actions/setup-java#1186</a></li>
</ul>
<p><strong>Full Changelog</strong>: <a
href="https://github.com/actions/setup-java/compare/v5.6.0...v5.7.0">https://github.com/actions/setup-java/compare/v5.6.0...v5.7.0</a></p>
</blockquote>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="b6effb05e4"><code>b6effb0</code></a>
Deprecate legacy Adopt distributions in v5 (<a
href="https://redirect.github.com/actions/setup-java/issues/1186">#1186</a>)</li>
<li><a
href="e498d2a66a"><code>e498d2a</code></a>
Backport <a
href="https://redirect.github.com/actions/setup-java/issues/1151">#1151</a>:
Fix missing wrapper cache distributions (<a
href="https://redirect.github.com/actions/setup-java/issues/1153">#1153</a>)</li>
<li><a
href="6a3384db74"><code>6a3384d</code></a>
Fix npm audit failures on releases/v5 (<a
href="https://redirect.github.com/actions/setup-java/issues/1154">#1154</a>)</li>
<li>See full diff in <a
href="https://github.com/actions/setup-java/compare/v5.6.0...v5.7.0">compare
view</a></li>
</ul>
</details>
<br />


[![Dependabot compatibility
score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=actions/setup-java&package-manager=github_actions&previous-version=5.6.0&new-version=5.7.0)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)

Dependabot will resolve any conflicts with this PR as long as you don't
alter it yourself. You can also trigger a rebase manually by commenting
`@dependabot rebase`.

[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)

---

<details>
<summary>Dependabot commands and options</summary>
<br />

You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits
that have been made to it
- `@dependabot show <dependency name> ignore conditions` will show all
of the ignore conditions of the specified dependency
- `@dependabot ignore this major version` will close this PR and stop
Dependabot creating any more for this major version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop
Dependabot creating any more for this minor version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop
Dependabot creating any more for this dependency (unless you reopen the
PR or upgrade to it yourself)


</details>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-08-09 15:33:46 -10:00
flexi767
14c8793df5 fix(engine): auto-restart tunnels that exit unexpectedly (#3393)
## Symptom

A tunnel process that dies unexpectedly (crash, OOM, network hiccup, the
24h `maxLifetimeMs` supervision cutoff) leaves `TunnelProcessManager` in
a terminal `failed` state. `handleUnexpectedExit` records the error and
stops — the operator's remote-access tunnel silently goes dark until a
human notices and restarts it.

## Fix

The manager now remembers the desired tunnel (`provider` + `config`)
across the start/switch lifecycle and respawns it after an unexpected
exit with exponential backoff — 1s base doubling to a 30s cap, both
configurable via new `restartBaseDelayMs` / `restartMaxDelayMs` options,
and `autoRestart: false` to opt out. The attempt counter resets once the
tunnel reports ready.

Intentional shutdowns stay shut down: `stop()` and `switchProvider()`
clear the desired tunnel and cancel any pending restart timer.

Hardening that falls out of restart support:
- Exit/spawn-error/readiness-timeout handlers are guarded by
process-handle identity, so a stale `close` event from a superseded
child cannot clobber the state of the restarted tunnel.
- The readiness-timeout path now SIGTERMs the stalled child instead of
leaking it while marking the tunnel failed.
- A synchronous `superviseSpawn` throw now records a redacted
`start_failed` status instead of escaping unhandled.

## Verification

- New tests (fake timers, no real waits): restart after unexpected exit
for both cloudflare and tailscale providers; backoff progression and
delay cap across repeated pre-readiness failures; explicit `stop()`
cancels a pending restart.
- `pnpm --filter @fusion/engine exec vitest run
src/__tests__/tunnel-process-manager.test.ts` — 17/17 pass.
- `pnpm verify:fast` — 14 steps green (scoped typecheck/build + CLI
build + boot smoke).
- Changeset included (`patch`, category `fix`).

## Provenance

This is the tunnel half of a fork-side fix (FN-915) that has been
running in production since July; the merge-blocker-preservation half of
that same fix already landed upstream (present since v0.76.0-beta.0).
Ported onto current `main` — the original patch applied cleanly.

🤖 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**
  * Tunnels now automatically restart after unexpected crashes.
  * Restart attempts use increasing delays up to a configurable maximum.
  * Automatic recovery is enabled by default and can be configured.

* **Bug Fixes**
  * Prevented stale process events from triggering unwanted restarts.
* Explicit stops and provider changes now cancel pending restart
attempts.
  * Failed or unready tunnel processes are terminated cleanly.

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

Co-authored-by: v <v@v.speedport.ip>
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
2026-08-09 15:23:59 -10:00
Phil Larson
b28b6d1053 fix: fail closed on incomplete external checkout routes (#3401)
## Summary
- fail closed when a persisted external remediation route lacks a
concrete checkout path
- verify recovery, remediation, dependency-abort cleanup, and completion
validation use the live persisted task route
- use unique missing-checkout fixtures and exact observed-path
assertions

## Test plan
- `pnpm --filter @fusion/engine exec vitest run
src/__tests__/verify-worktree-invariants-missing.test.ts
src/__tests__/executor-triage-column-audit.test.ts --silent=passed-only
--reporter=dot`
- `pnpm --filter @fusion/engine exec vitest run
src/__tests__/executor-fast-mode-workflows.test.ts -t 'completed-task
recovery captures the live external checkout|pre-merge remediation'
--silent=passed-only --reporter=dot`
- `pnpm --filter @fusion/engine typecheck`
- `pnpm exec eslint packages/engine/src/executor.ts
packages/engine/src/__tests__/verify-worktree-invariants-missing.test.ts
packages/engine/src/__tests__/executor-triage-column-audit.test.ts
packages/engine/src/__tests__/executor-fast-mode-workflows.test.ts`
- `pnpm check:fnxc-future-dates`
- `pnpm check:changesets --strict`
- `git diff --check`


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

## Summary by CodeRabbit

* **Bug Fixes**
* Improved recovery for externally executed tasks by using the latest
routing information instead of outdated task data.
* External remediation now stops safely when a checkout location is
missing or invalid, preventing execution in an unintended location.
  * Improved cleanup behavior to preserve operator-owned checkouts.
* Enhanced validation and error reporting for missing or invalid
checkout paths.
* **Tests**
* Expanded coverage for recovery, remediation safety, checkout
ownership, and worktree validation scenarios.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2026-08-09 15:23:39 -10:00
gsxdsm
e573178e31 fix(agents): never let a built-in workflow role agent be unroutable
The board stopped moving. Work items churned held -> running -> held at
~3.5/sec across every task, pinning a core and writing ~19k workflowWorkItem
audit rows/hour while nothing executed. Hold reason:
workflow-principal-role-pool-exhausted:executor.

provisionBuiltinWorkflowRoleAgents seeded the four permanent owners (triage,
executor, reviewer, merger) with runtimeConfig.enabled=false, while the
router's available() treats enabled===false as unavailable. The only permanent
principals for every built-in role were unroutable BY CONSTRUCTION — shipped
that way, so any instance without operator-created role agents deadlocks at its
first workflow node. Nothing self-recovers: a pool only changes by operator
action.

Routability of these four is an invariant, not a setting. Unlike an operator's
agent, disabling one does not opt an agent out — it removes the only thing that
can run that stage, and there is no fallback.

- seed built-ins enabled; converge existing rows on provisioning
- enforceBuiltinWorkflowRoleRoutability coerces enabled back at the durable
  writeAgent seam, so no REST/UI/plugin/restore path can reintroduce the
  deadlock. Other runtimeConfig keys are preserved; operator-owned agents keep
  their off switch
- share the static routability predicate (isWorkflowPrincipalEligible) between
  provisioning and the router so the two cannot drift apart again

Also fix the spin itself: a principal hold had no cooldown, so the scheduler
re-dispatched instantly and the run re-entered only to re-fence and re-park.
It now records a backoff ladder (15s -> 5m) checked before graph entry, and
logs once per distinct reason instead of every pass — the same self-recovering
shape as holdForSessionContention. The hold never increments `attempt`, so no
existing guard could ever fire.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-09 18:22:01 -07:00
Phil Larson
73f428c5bf fix: preserve orphaned pinned worktrees before recovery (#3380)
## Summary

- recover task-ID-pinned worktrees when their directory remains but Git
metadata/registration is gone
- preserve the orphan directory under `.fusion/recovery/worktrees`
before recreating the worktree
- retain existing fail-closed behavior for active, foreign, repo-root,
or out-of-root paths

## Problem

A task-pinned worktree can lose `.git` metadata while leaving build
artifacts behind. Fusion classifies that path as incomplete or
unregistered, but then calls `git worktree remove --force`. Git cannot
remove a directory it no longer recognizes as a worktree, so acquisition
aborts and the scheduler can repeat the same recovery indefinitely.

The observed reproduction left `.build` and `.swiftpm` under the pinned
path after Git registration was gone.

## Fix

For an inactive path that is both:

1. inside the configured worktree root, and
2. classified as incomplete or unregistered,

hold the shared worktree-path reservation across classification,
preservation, and recreation. Recovery directories are created one
canonical, project-contained component at a time, then the orphan is
atomically moved into `.fusion/recovery/worktrees` and the pinned
worktree is recreated. Moving rather than deleting preserves any unknown
task artifacts for operator inspection. Other classifications continue
through the existing guarded Git-removal path.

## Verification

- `pnpm --filter @fusion/engine exec vitest run
src/__tests__/worktree-acquisition.test.ts --silent=passed-only
--reporter=dot` — 30 passed
- `pnpm --filter @fusion/engine typecheck`
- `pnpm --filter @fusion/engine build`
- `pnpm test:gate:static`
- `pnpm check:changesets`
- `git diff --check`


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

- **Bug Fixes**
- Improved recovery of task-pinned worktrees when incomplete or stale
directories occupy the expected location.
- Preserves eligible inactive or unregistered worktree contents in a
recovery area instead of deleting them.
- Prevents unsafe recovery through symbolic links and handles concurrent
recovery attempts reliably.
  - Supports recovery when directories span different storage devices.
- Ensures interrupted or invalid worktree states can be recreated safely
without disrupting active sessions.

- **Documentation**
- Added a patch changeset documenting the improved worktree recovery
behavior.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2026-08-09 15:06:36 -10:00