Commit Graph

9795 Commits

Author SHA1 Message Date
gsxdsm
6745126e14 Merge branch 'main' into fix/workspace-git-init-skip 2026-06-23 23:21:35 -07:00
gsxdsm
a9ea1f6fe3 fix(workspace): skip git init for workspace-mode project roots
ensureGitRepositoryForProjectPath unconditionally ran `git init` on
non-git paths, including workspace roots. This created a stray empty
repo with unborn HEAD at the workspace root, poisoning every downstream
git command (executor session cwd: `fatal: ambiguous argument 'HEAD'`).

Add an early-return guard that checks loadWorkspaceConfig before any git
operation, keeping the workspace root non-git as intended by the
workspace execution contract.
2026-06-23 23:14:05 -07:00
gsxdsm
ba65fceba1 feat(workspace): Phase D — self-healing reconcilers + e2e harness (U8/U9) (#1718)
> ⚠️ **Draft — do not merge until the stack lands.** Final phase; stacks
on foundation #1710 + U0 #1711 + Phase A #1713 + Phase B #1714 + Phase C
#1717; targets `main` with the whole stack. **Review only the Phase-D
commits** (`7cd204e` U1, `78d7a28` U2, `8e70d69` review fixes).

## Workspace mode — Phase D (self-healing + e2e) — FINAL PHASE

Implements Phase D of the [master
plan](docs/plans/2026-06-21-002-feat-workspace-mode-execution-model-plan.md)
— units U8/U9 ([Phase-D
plan](docs/plans/2026-06-22-001-feat-workspace-phase-d-plan.md)). Closes
the workspace-mode lifecycle: the self-healing layer is now
**workspace-aware** (it was either wrongly finalizing or silently
skipping workspace tasks), and an **e2e harness** proves the whole path
with no remote push. After this the feature is operationally complete.

### What changed
- **U1 — workspace-aware self-healing.** The feasibility pre-check
caught a **P0**: `landWorkspaceTask` sets status `"merging"`, and the
existing `recoverInterruptedMergingTasks` would (via the singular
`findLandedTaskCommit`) finalize a **partial-landed** workspace task to
done on *one* repo's commit. Fixed — and the review's FN-5893 audit
found and gated **four more** single-commit-finalize sites
(`recoverStuckMergeDeadlocks`, `recoverOrphanOnlyScopeViolations`,
`recoverAlreadyMergedReviewTasks`,
`recoverBranchMisboundInReviewTasks`). `recoverMergeableReviewTasks` now
admits workspace tasks (it gated on `Boolean(task.worktree)`, null for
them). Three new reconcilers: partial-land recovery (re-enqueue via the
idempotent `enqueueMerge`, guarded + starvation-bounded), phantom
`workspace-repo-land` lease reclaim (new
`activeSessionRegistry.entriesByKind` seam, terminal-owner-only), and
orphaned per-repo worktree cleanup (from the **stored** `worktreePath`,
no temp-root walk). A workspace-aware liveness predicate + an
`isMergePending` merge-queue guard prevent moving a live/dispatching
task backward.
- **U2 — e2e harness.** A real two-repo lifecycle test (engine-default
lane, `describeIfGit`) asserting the **no-push invariant** directly
(snapshot every origin/remote-tracking ref before+after
`landWorkspaceTask`, assert byte-for-byte equality while local refs
advance) plus per-repo `landedSha`, finalize-once, and partial-land
recovery through the actual reconciler (no double-land).

### Review (4 personas)
**No P0 shipped.** The reviewers verified the backward-safety invariants
and caught the FN-5893 all-surfaces miss (the P0 guard had been applied
to one of several finalize paths) and a TOCTOU (the reconciler was blind
to the merge-queue dispatch window → a same-task
double-`landWorkspaceTask` could double-squash). Both fixed; every guard
reuses `allowsAutoMergeProcessing` (FN-5147) + the workspace
liveness/merge-pending checks and only ever skips (`-no-action`), never
moves a live or human-gated task backward.

### Deferred
- Extracting `self-healing-workspace.ts` (the file is 10.7k lines) and
`workspace-merger.ts` (Phase-C residual). Per-sub-repo cwd reachability
verification; store-level atomic per-repo merge; rich dashboard per-repo
merge UI. Remote push (out — D2/D5 local-ref only).

### Verification
Gate green: lint, typecheck (29 projects), build, `test:gate` (649+58);
self-healing + e2e + project-engine + merger **724**.

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


<!-- stage-review-badge-begin -->

---

<a href="https://stagereview.app/Runfusion/Fusion/pull/1718">
  <picture>
<source media="(prefers-color-scheme: dark)"
srcset="https://stagereview.app/assets/gh-open-in-stage-dark.svg">
<img src="https://stagereview.app/assets/gh-open-in-stage-light.svg"
alt="Open in Stage">
  </picture>
</a>

<!-- stage-review-badge-end -->

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

## Release Notes

* **New Features**
* Workspace multi-repository merges now land per-repository with
automatic recovery if individual repos fail.
* Dashboard and CLI merge operations now fully support workspace tasks.

* **Bug Fixes**
* Improved reliability of partial-land recovery with bounded retry logic
and state persistence.
* Fixed race conditions in concurrent merge coordination across
sub-repositories.

* **Deprecations**
* Deterministic merge mode is deprecated; all merges now use the unified
AI merge path.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2026-06-23 21:02:15 -07:00
gsxdsm
59d5e81a95 Merge remote-tracking branch 'origin/main' into latest4-1718
# Conflicts:
#	packages/engine/src/__tests__/merge-error-recovery.test.ts
#	packages/engine/src/merger-ai.ts
2026-06-23 20:58:31 -07:00
gsxdsm
01551ee233 feat(workspace): Phase C — per-repo merge loop, land-as-you-go on local integration refs (U5/U6/U7) (#1717)
> ⚠️ **Draft — do not merge until the stack lands.** Stacks on
foundation #1710 + U0 #1711 + Phase A #1713 + Phase B #1714; targets
`main` with the whole stack diff. **Review only the Phase-C commits**
(`744ed09` U1, `7544346` U2, `64e87f9` U3, `627bdcf` review fixes).

## Workspace mode — Phase C (per-repo merge loop, land-as-you-go on
local integration refs)

Implements Phase C of the [master
plan](docs/plans/2026-06-21-002-feat-workspace-mode-execution-model-plan.md)
— units U5/U6/U7 ([Phase-C
plan](docs/plans/2026-06-21-006-feat-workspace-phase-c-plan.md)).
Replaces U0's R7 guard (which *threw* on workspace-task merges) with the
real **per-repo land loop**. A workspace task now runs → captures →
reviews → **merges**: each acquired sub-repo's `fusion/<id>` branch
lands onto **that repo's own LOCAL integration ref** (CAS +
fast-forward, **no remote push** — D2/D5), land-as-you-go.

### What changed
- **U1 — `landOneRepo` + `landWorkspaceTask`.** Extracted the per-repo
land mechanics out of `runAiMerge`'s inline clean-room closure into an
exported `landOneRepo`; `runAiMerge` is now its byte-for-byte
single-repo caller (the 56-test merger-ai oracle stays green).
`landWorkspaceTask` loops the acquired sub-repos (sorted), re-resolves
each repo's integration branch (override-stripped → own `origin/HEAD`),
lands each, aggregates repo-tagged results. The engine dispatch +
user-facing CLI `fn task merge`/dashboard merge doors route workspace
tasks here; `store.mergeTask`/`aiMergeTask`/the `runAiMerge` chokepoint
stay throwing (defense-in-depth).
- **U2 — landed predicate + finalize-once + auto-retry/park.** Each
landed repo's tip is persisted as `workspaceWorktrees[repo].landedSha`;
`isRepoLanded` skips it on retry (idempotent). The task finalizes to
done exactly once after *all* repos land. A partial land raises
`WorkspacePartialLandError` → the engine consumes a `mergeRetry` and
re-runs (skipping landed repos) up to MAX, then operator-parks.
- **U3 — per-repo land lease.** A new `activeSessionRegistry`
`workspace-repo-land` kind serializes concurrent same-sub-repo lands.

### Review (5 personas)
**No P0.** Adversarial verified the two crown-jewel invariants clean:
**no remote push** anywhere in the land loop (CAS + FF-only), and
**exactly one mergeRetry per attempt** with the hard-fail guard unable
to enter the retry loop. Fixed in-branch (`627bdcf`): a **double-land**
bug (a swallowed `landedSha` write could produce a second squash commit
— now propagated, with a `Fusion-Task-Id`-trailer landed-fallback); a
**cross-phase lease clobber** (a merging task could overrun an executing
task's acquire lease — now `taskId`-aware); a **retry storm** under DB
outage (now fails closed); a **status `'merging'` leak**; a
**reachability-gate poison** that demoted fully-merged workspace tasks
(the fast-path now skips them — they're verified by per-repo
`landedSha`); the **dashboard `merged:false`** contradiction;
busy-contention no longer burns the retry quota; backoff capped;
`WorkspacePartialLandError` promoted to a real class.

### Deferred to Phase D
- Self-healing reconcilers for **partial-landed / stuck** workspace
merges (the landed predicate `isRepoLanded` is exported for this).
- The e2e workspace harness.
- Per-repo worktree teardown; extracting a `workspace-merger.ts` module
(`merger-ai.ts` is large); per-sub-repo cwd reachability verification.

### Verification
Gate green: lint, typecheck (29 projects), build, `test:gate` (649+58);
workspace-merger + merger-ai oracle + project-engine **174**.

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


<!-- stage-review-badge-begin -->

---

<a href="https://stagereview.app/Runfusion/Fusion/pull/1717">
  <picture>
<source media="(prefers-color-scheme: dark)"
srcset="https://stagereview.app/assets/gh-open-in-stage-dark.svg">
<img src="https://stagereview.app/assets/gh-open-in-stage-light.svg"
alt="Open in Stage">
  </picture>
</a>

<!-- stage-review-badge-end -->

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

* **New Features**
* Workspace merges now process repositories independently, tracking
per-repo completion for idempotent retries.
* Concurrent land operations on the same sub-repository are serialized
to prevent conflicts.
* Dashboard and CLI now report full merge status for workspace tasks,
including per-repository outcomes.

* **Bug Fixes**
* Improved handling of partial workspace land failures with proper
backoff and retry logic.
  * Stricter validation of workspace configuration repository arrays.

* **Documentation**
  * Added comprehensive Phase C plan for workspace merge behavior.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2026-06-23 20:34:17 -07:00
gsxdsm
b464f7d7d4 Merge remote-tracking branch 'origin/main' into latest3-1717 2026-06-23 19:58:12 -07:00
gsxdsm
4a1fd88d57 Merge remote-tracking branch 'origin/main' into latest3-1718 2026-06-23 19:58:03 -07:00
gsxdsm
ddbb58bfc8 feat(workspace): Phase B — per-repo capture, contamination, review, completion verify (U3/U4) (#1714)
> ⚠️ **Draft — do not merge until the stack lands.** Stacks on the
workspace foundation (#1710) + U0 (#1711) + Phase A (#1713). Targets
`main`; its diff **includes the whole stack**. **Review only the Phase-B
commits** (`fc9423e` U1, `81edbee` U2, `453ed92` review fixes). Retarget
once the stack merges.

## Workspace mode — Phase B (per-repo capture, contamination, review,
completion verify)

Implements Phase B of the [master
plan](docs/plans/2026-06-21-002-feat-workspace-mode-execution-model-plan.md)
— units U3, U4 ([Phase-B
plan](docs/plans/2026-06-21-005-feat-workspace-phase-b-plan.md)). The
executor's change-capture, contamination, worktree-invariant, review,
and completion-verify paths now iterate `task.workspaceWorktrees` **per
sub-repo**, each against that repo's own `baseCommitSha` (Phase A), with
repo-prefixed file lists. After Phase B a workspace task can run **and**
be captured + reviewed + completion-verified — short of merge (Phase C).

A feasibility pre-check corrected the plan before implementation:
capture/contamination/scope-leak were **not gated** — they silently
degraded to empty against the non-git root. Phase B adds the missing
workspace branches and **reuses the existing `captureModifiedFiles`**
machinery per repo (its `resolveDiffBaseRef` merge-base fallback handles
undefined per-repo bases; its `filterFilesToOwnTaskCommits` divergence
audit restores contamination for free) rather than hand-building diffs.

### What changed
- **U1 — per-repo capture + verify.** Post-session capture loops
`workspaceWorktrees`, reusing `captureModifiedFiles(repo.worktreePath,
repo.baseCommitSha, …)` and repo-prefixing into `task.modifiedFiles`;
branch-attribution runs per sub-repo. `verifyWorktreeInvariants`
un-stubbed to iterate every acquired worktree, **preserving its
`{ok|reason|observed|expected}` union** (the `reason` enum drives
requeue/handoff) with an additive `repo` field. The no-op
`assertCleanBranchAtBase` is not iterated.
- **U2 — per-repo review (both sites) + completion verify.** A shared
`reviewWorkspacePerRepo` loops the existing single-cwd `reviewStep` once
per sub-repo (the reviewer agent runs its own `git diff` at that cwd —
**N reviewer agents**, an accepted cost) and aggregates repo-tagged
verdicts as a **conjunction**. Both entry points iterate —
`fn_review_step` and the step-inversion seam (FN-5893). `fn_task_done`
runs the per-repo `verifyWorktreeInvariants` and a per-repo
`evaluateTaskDoneScopeLeak`. New `workspace-paths.ts` repo-prefix
helper.

### Review
4-persona `ce-code-review`. **No P0**; the review conjunction was
confirmed safe (no false-done — empty map and per-repo throws both route
to UNAVAILABLE → blocks). Fixed in-branch: the scope-leak guard now
**fails closed** on a per-repo throw (was fail-open) and **blocks a
scoped task that acquired zero worktrees** (was silently passing); the
review loop breaks on first non-APPROVE (preserving the verdict); the
`.changeset` always-allowed carve-out is honored in workspace mode
(wiring in the repo-local helper); deterministic offending-repo
ordering. Verified safe: semaphore release on throw + reviewer abort via
session disposal.

### Deferred to Phase C
- Extract a `workspace-executor.ts` module (executor.ts is 16k+ lines) —
to land **at the start of Phase C, before the merge loop**
(maintainability P1).
- Committed off-scope changes can still slip past per-repo scope-leak
when a repo's `baseCommitSha` is undefined and no merge-base resolves
(`captureModifiedFiles` returns `[]`) — partially pre-existing,
amplified by treating undefined base as normal.
- Store-level **atomic** per-repo `workspaceWorktrees` merge (becomes
reachable here with multi-repo acquisition).
- The merge loop / landed predicate / file-scope leases (master
U5/U6/U7).

### Verification
Gate green: lint, typecheck (29 projects), build, `test:gate` (649+58);
workspace tests 28+ (capture, review conjunction, fail-closed scope
guard, zero-acquire block, `.changeset` carve-out).

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


<!-- stage-review-badge-begin -->

---

<a href="https://stagereview.app/Runfusion/Fusion/pull/1714">
  <picture>
<source media="(prefers-color-scheme: dark)"
srcset="https://stagereview.app/assets/gh-open-in-stage-dark.svg">
<img src="https://stagereview.app/assets/gh-open-in-stage-light.svg"
alt="Open in Stage">
  </picture>
</a>

<!-- stage-review-badge-end -->

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

## Release Notes

* **New Features**
* Added workspace mode support for multi-repository task execution with
per-repo worktree management.
* Dashboard now displays workspace task status with "N repos acquired"
summary and per-repo worktree details in task cards and detail modals.

* **Bug Fixes**
* Fixed workspace tasks rendering blank on the dashboard; task cards and
detail views now display acquired repository information.

* **Improvements**
* Enhanced workspace acquisition reliability with identity guards,
per-repo base commit tracking, and serialized acquisition controls to
prevent conflicts.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2026-06-23 18:24:55 -07:00
gsxdsm
216f910524 Merge remote-tracking branch 'origin/main' into latest2-1718
# Conflicts:
#	packages/engine/src/agent-tools.ts
#	packages/engine/src/worktree-acquisition.ts
2026-06-23 18:15:24 -07:00
gsxdsm
cd178261ec Merge remote-tracking branch 'origin/main' into latest2-1717
# Conflicts:
#	packages/engine/src/__tests__/executor-workspace.test.ts
#	packages/engine/src/agent-tools.ts
#	packages/engine/src/base-commit-capture.ts
#	packages/engine/src/worktree-acquisition.ts
2026-06-23 18:13:41 -07:00
gsxdsm
078723febb Merge remote-tracking branch 'origin/main' into latest2-1714
# Conflicts:
#	packages/engine/src/agent-tools.ts
#	packages/engine/src/base-commit-capture.ts
#	packages/engine/src/worktree-acquisition.ts
2026-06-23 18:11:26 -07:00
gsxdsm
afa388a865 feat(#1675): add X-Session-Id and X-Session-Affinity routing headers to LLM requests (#1736)
Closes #1675

## Summary

Adds `X-Session-Id` and `X-Session-Affinity` request headers to all
outbound LLM chat completion requests. These headers are widely
understood by LLM gateways, proxies, and observability tooling:

- **Gateway sticky routing** — keep consecutive requests from one
conversation on the same backend or cache instance
- **Observability trace grouping** — tools like Langfuse and Arize group
individually stateless API calls into a single cohesive multi-turn chat
trace
- **Memory/proxy middleware** — fetch and append conversation history
matching the session id

Both headers carry the same stable identifier: the **task id** when
available (stable across pause/resume), otherwise the **pi session id**
for non-task sessions (chat, summarizer, reviewer).

## Implementation

The headers are injected by wrapping `modelRegistry.getApiKeyAndHeaders`
— the single chokepoint pi-coding-agent uses to resolve per-request auth
and headers for both the main stream and compaction. Verified against
the pi-coding-agent source (`sdk.js:171`,
`agent-session.js:145/162/1490`) that all HTTP-based provider paths
route through this method. The wrapper merges routing headers into the
resolved output, preserving provider-specific headers and never
disturbing API-key resolution.

The `modelRegistry` is created fresh per `createFnAgent` call, so the
mutation is session-scoped.

## Changes

- **`packages/engine/src/pi.ts`** — `buildSessionRoutingHeaders()` and
`attachSessionRoutingHeaders()` helpers; wiring call inside
`createFnAgent` resolving `sessionRoutingId = options.taskId ??
piSessionId`. Warns (instead of silently no-oping) if the pi API changes
and `getApiKeyAndHeaders` is absent.
- **`packages/engine/src/executor.ts`** — propagate `taskId` to four
secondary task-scoped sessions (retry, verification-fix, workflow-step,
child-agent) that previously fell back to a per-instance pi id,
fragmenting per-task observability grouping.
- **`packages/engine/src/__tests__/pi-session-routing-headers.test.ts`**
— unit tests for the helpers (header shape, merge, failed-auth
passthrough, absent-method no-op).
- **`packages/engine/src/__tests__/pi-create-fn-agent.test.ts`** —
end-to-end wiring tests asserting the `taskId ?? piSessionId` precedence
inside `createFnAgent`.
- **`.changeset/session-routing-headers.md`** — `@runfusion/fusion:
minor` (new feature).

## Test plan

- [x] Engine typecheck clean
- [x] ESLint clean
- [x] `pi-session-routing-headers.test.ts` (5 tests)
- [x] `pi-create-fn-agent.test.ts` wiring tests (3 new, 79 total)
- [x] Engine-core gate suite (610 tests)
- [x] Executor + pi test suites (946 tests)

<!-- stage-review-badge-begin -->

---

<a href="https://stagereview.app/Runfusion/Fusion/pull/1736">
  <picture>
<source media="(prefers-color-scheme: dark)"
srcset="https://stagereview.app/assets/gh-open-in-stage-dark.svg">
<img src="https://stagereview.app/assets/gh-open-in-stage-light.svg"
alt="Open in Stage">
  </picture>
</a>

<!-- stage-review-badge-end -->

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

* **New Features**
* Added session-routing HTTP headers (`X-Session-Id`,
`X-Session-Affinity`) to all outbound LLM chat completion requests.
* Uses a stable routing identifier derived from `taskId` when available
(preserving consistency across pause/resume), otherwise falls back to
the current session id.
* Ensures follow-up and spawned sessions reuse the same routing id for
consistent sticky routing and trace correlation.
* **Tests**
* Added coverage to verify header construction, merge behavior, and
fallback/no-op cases.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2026-06-23 18:03:37 -07:00
gsxdsm
0ebc9ea252 Merge branch 'main' into feature/add-request-headers-x-session-id-and-x-session-a 2026-06-23 18:03:33 -07:00
gsxdsm
4745f3bd79 feat(workspace): Phase A — session scoping, per-repo acquisition, dashboard floor (U1/U2/U10) (#1713)
> ⚠️ **Draft — do not merge until the stack lands.** Stacks on the
workspace foundation (#1710) + U0 (#1711), which live on a fork and
can't be PR bases here, so this targets `main` and its diff **includes
#1710 + #1711 + Phase A**. **Review only the Phase-A commits**
(`09bd01b` U1, `023e4b0` U3, `12d33c5` U2, `d5fa865` review fixes).
Retarget to a clean Phase-A-only diff once the stack merges.

## Workspace mode — Phase A (make a workspace task *run*)

Implements Phase A of the [master
plan](docs/plans/2026-06-21-002-feat-workspace-mode-execution-model-plan.md)
— units U1, U2, U10 ([Phase-A
plan](docs/plans/2026-06-21-004-feat-workspace-phase-a-plan.md)). A
workspace task (Project rootDir = non-git parent of sub-repos) can now
acquire per-repo worktrees and browse/edit in them. Capture/review/merge
are Phases B–D.

Settled design (from the master plan): **land-as-you-go on each repo's
LOCAL integration ref** (no remote push), session-time coherence. The R7
merge-boundary guard from U0 stays at the merge chokepoint; Phase A
doesn't route around it.

### What changed
- **U1 — executor session scoping.** In workspace mode the executor
skips the root `acquireTaskWorktree` and every rootDir git preflight
(base-commit, contamination, identity-guard,
`verifyWorktreeInvariants`), roots the agent session at the non-git
workspace root (browse-only; `task.worktree` unset), and tracks
`activeWorktrees` as a per-task `Set<path>` — every consumer converted
to membership semantics (incl. `listWorktreeHolders` flat-mapping N
holder rows, the unregister resolvers looping all paths). Per-repo
acquired paths are registered into the Set via the
`fn_acquire_repo_worktree` tool. Non-workspace path byte-for-byte
unchanged. The foundation's self-mocking test was replaced with a real
two-repo git fixture harness reused by later units.
- **U2 — per-repo acquisition hardening.**
`acquireWorkspaceRepoWorktree` now installs the identity guard (executor
parity args), captures a per-repo `baseCommitSha` **local-first**
against the repo's own resolved integration branch (stripping the shared
`integrationBranch`/`baseBranch` override so each sub-repo falls through
to its own `origin/HEAD`), persists it into `workspaceWorktrees[repo]`,
and registers same-sub-repo exclusivity via `activeSessionRegistry`
path-keying (a recycle pool isn't a cross-task lock). Post-acquire steps
are non-fatal; idempotent re-acquire.
- **U10 — dashboard floor.** Workspace tasks render a placeholder / flat
per-repo list instead of a blank card; CONCEPTS.md notes the non-atomic
merge semantics.

### Review
5-persona `ce-code-review` (correctness, adversarial, reliability,
api-contract, maintainability). **No P0** — the workspace-root-removal
path was specifically ruled out, and the contract additions
(`baseCommitSha?`, `GitMutationType`/`ActiveSessionKind` unions,
optional base-capture param, `WorkspaceRepoAcquireBusyError`) verified
additive/non-breaking. Corroborated P1s fixed in-branch:
`fn_acquire_repo_worktree` now returns a sanitized retryable error (was
an uncaught throw); per-repo paths are actually added to
`activeWorktrees`; post-acquire failures no longer strand the worktree.
Plus P2s (baseBranch strip, sequential-acquire clobber, busy-path
logging, memo key-set, observability).

### Deferred to Phase B (by scope)
Per-repo worktree teardown on completion, `scanIdleWorktrees` orphan
coverage for sub-repo worktrees, FN-6782 reaper dedup of multi-row
holders, a store-level atomic per-repo `workspaceWorktrees` merge. Also:
don't run a workspace task end-to-end until Phase B re-adds per-repo
contamination/verify.

### Verification
Gate green: lint, typecheck (29 projects), build, `test:gate` (649 +
58); workspace tests 25, dashboard 251.

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


<!-- stage-review-badge-begin -->

---

<a href="https://stagereview.app/Runfusion/Fusion/pull/1713">
  <picture>
<source media="(prefers-color-scheme: dark)"
srcset="https://stagereview.app/assets/gh-open-in-stage-dark.svg">
<img src="https://stagereview.app/assets/gh-open-in-stage-light.svg"
alt="Open in Stage">
  </picture>
</a>

<!-- stage-review-badge-end -->

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

## Release Notes

* **New Features**
* Workspace mode execution now properly displays task status in
dashboard with "N repos acquired" placeholder.
* Improved workspace task acquisition with per-repository exclusivity
controls and automatic retry logic.

* **Bug Fixes**
* Fixed blank rendering of workspace tasks in dashboard and detail
views.
* Enhanced error handling for workspace task acquisition failures with
improved visibility.

* **Documentation**
* Updated workspace mode execution plan with implementation details and
phase breakdown.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2026-06-23 17:56:04 -07:00
gsxdsm
1293f3432d Merge remote-tracking branch 'origin/main' into latest-1718
# Conflicts:
#	packages/engine/src/executor.ts
2026-06-23 17:41:30 -07:00
gsxdsm
9fbc93c8b1 Merge remote-tracking branch 'origin/main' into latest-1717
# Conflicts:
#	packages/engine/src/executor.ts
2026-06-23 17:40:35 -07:00
gsxdsm
0b111bbb03 Merge remote-tracking branch 'origin/main' into latest-1714
# Conflicts:
#	packages/engine/src/executor.ts
2026-06-23 17:39:37 -07:00
gsxdsm
fa622721e8 Merge remote-tracking branch 'origin/main' into latest-1713
# Conflicts:
#	packages/engine/src/executor.ts
2026-06-23 17:38:37 -07:00
gsxdsm
8d343b87b3 Merge branch 'main' into feature/add-request-headers-x-session-id-and-x-session-a 2026-06-23 17:37:57 -07:00
gsxdsm
e3c7e1d42f feat(FN-6880): graph-native optional steps + add-on subgraphs (and FN-6879 node help) (#1712)
## Summary

Two workflow-editor improvements (the branch-group work this branch is
named for is already on `main`):

### FN-6880 — Graph-native optional steps + add-ons as subgraphs
Optional steps move from an execution-inert *declaration*
(`optionalSteps: [{templateId}]` run through a hidden `workflow-step`
seam) to a real graph construct:

- **`optional-group` container node** (mirrors `foreach`/`loop`) holding
a `template:{nodes,edges}` subgraph. The graph executor runs it **once**
when the group is enabled for a task and **bypasses** it (passes
through, runs no template node) when disabled. (U1 IR+validation, U2
executor)
- **Enable state reuses the per-task `enabledWorkflowSteps` facet** + a
workflow-level `defaultOn`, keyed by the group node id; new tasks seed
from `defaultOn`. (U3)
- **Editor authoring**: the container is a registered React-Flow group
node with a `defaultOn` toggle; round-trips through
`flowToIr`/`irToFlow`. (U4)
- **All 7 built-in add-ons** (documentation-review, qa-check,
security-audit, performance-review, accessibility-check,
browser-verification, frontend-ux-design) are insertable from the
palette as a node **or** wrapped in an optional-group. (U5)
- **Built-ins migrated**: coding + stepwise-coding express
`browser-verification` as an optional-group (default OFF); coding is now
interpreter-deferred. Parity oracles updated. (U6)
- **Legacy declaration surface retired** (U7a): the
`WorkflowOptionalStep` type + `optionalSteps` IR field +
`validateOptionalSteps`, and the editor's declaration authoring UI
(`WorkflowOptionalStepsPanel`, `optionalStepsOf`, `flowToIr` threading).
A legacy persisted `optionalSteps` key is tolerated at parse. The
per-task toggle surfaces (dropdown, inline card, modal, Workflow tab)
are unchanged — they consume the distinct
`ResolvedWorkflowOptionalStep`.

### FN-6879 — Workflow node Help in the detail pane
Every node kind (including engine-managed graph-only nodes: merge gate,
branch-group integration/promotion, PR/recovery) gets an in-editor Help
section: what it does, how to configure it, inputs/outputs/edges.

## Code review
4 reviewers (correctness, testing, API-contract, reliability) over the
feature diff. One **P1** found and fixed: enabling a built-in
optional-group whose node id collides with a `WORKFLOW_STEP_TEMPLATES`
id was silently bypassed (the toggle id was remapped to a materialized
step row id the executor never matched) — fixed by passing
optional-group ids through enable resolution untouched, with
colliding-id regression tests on the create + update paths. Triaged P3s
(description/icon drop, switch-time seeding, selection-row id mix) are
benign/deferred.

## Verification
- Lint clean · `tsc --noEmit` 0 errors in core/engine/dashboard · `pnpm
build` succeeds · `pnpm test:gate` green (engine-core 649, ci-shape 58).
- Feature suites: core IR/validation/resolver/seeding, engine
run-once/bypass + two-task divergence + built-in execution, dashboard
editor round-trip + palette + node-help.

## Deferred (documented follow-ups)
- **`workflow-step` seam infrastructure removal** — the seam is a shared
`WorkflowSeam` union member woven through ~9 engine runtime files
(`runtime-primitives`, `step-session-executor`,
`workflow-node-handlers`, compiler seam-anchor, `runWorkflowSteps`). Now
orphaned (no built-in uses it) but inert; excising it is its own unit.
See the plan's Deferred section.
- Minor: optional-group `description`/`icon` on the toggle UI;
`defaultOn` seeding on post-create workflow switch.

## Not run
- Browser/visual testing was not run in this environment
(worktree-bundle dashboard setup). UI is covered by jsdom render tests
including the node-type registration test (the exact
stale-bundle/unregistered-type failure mode the team's learnings flag).
Recommend a real-browser pass before merge.

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


<!-- stage-review-badge-begin -->

---

<a href="https://stagereview.app/Runfusion/Fusion/pull/1712">
  <picture>
<source media="(prefers-color-scheme: dark)"
srcset="https://stagereview.app/assets/gh-open-in-stage-dark.svg">
<img src="https://stagereview.app/assets/gh-open-in-stage-light.svg"
alt="Open in Stage">
  </picture>
</a>

<!-- stage-review-badge-end -->

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

## Release Notes

**New Features**
- Optional-group container nodes now power optional workflow behavior
(run once per enabled task; bypass when disabled).
- Added a Help section to the workflow node inspector, including
“Engine-managed” read-only nodes.
- Added “insert as optional group” to wrap palette templates quickly.

**Improvements**
- Built-in coding/stepwise workflows now use optional-group for browser
verification.
- Task enablement supports optional-group `defaultOn` seeding.

**Bug Fixes**
- Fixed cases where optional-group toggles could be silently bypassed
when ids collide.

**Changes (Breaking)**
- Retired the legacy optional-step model and its authoring UI; migrate
to optional-group nodes.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2026-06-23 17:36:16 -07:00
gsxdsm
e17e9bc867 feat(#1675): add X-Session-Id and X-Session-Affinity routing headers to LLM requests
Add X-Session-Id and X-Session-Affinity headers to all outbound LLM chat
completion requests so LLM gateways can sticky-route consecutive requests
from the same conversation and observability tools (Langfuse, Arize) can
group stateless API calls into a single multi-turn trace.

The headers carry a stable identifier: the task id when available (stable
across pause/resume), otherwise the pi session id. The implementation wraps
modelRegistry.getApiKeyAndHeaders -- the single chokepoint pi-coding-agent
uses for both the main stream and compaction -- merging routing headers into
the resolved output. This covers all HTTP-based providers (built-in, custom,
and HTTP-streaming extensions) without disturbing auth resolution.

Also propagates taskId to four secondary executor sessions (retry,
verification-fix, workflow-step, child-agent) that previously fell back to
a per-instance pi id, fragmenting per-task observability grouping.

Closes #1675
2026-06-23 17:31:52 -07:00
gsxdsm
37b2cb38ac Merge branch 'main' into feature/workflow-branch-group 2026-06-23 17:16:16 -07:00
gsxdsm
be0cab1d43 fix(dashboard): raise model dropdown z-index above floating windows
Raise CustomModelDropdown portal z-index from 1200 to 11000 so model
selection popups render above the shared floating-window stack (10100+)
instead of being obscured by popped-out ChatView and other floating modals.
2026-06-23 16:47:38 -07:00
gsxdsm
e9a6955b49 FN-6939: add narrow dock preview modal
Adds an accessible preview modal path for constrained Dev Server right-dock layouts.

- Detect narrow direct right-dock hosts while preserving inline previews for full-page, mobile, and expanded modal hosts.
- Replace the crowded inline preview with a compact Open preview launcher and accessible modal controls in narrow docks.
- Add focused preview/mobile coverage, documentation, styling, and a published package changeset.

Files changed:
 .../fn-6939-dev-server-narrow-preview-modal.md     |   5 +
 docs/dashboard-guide.md                            |   3 +
 .../dashboard/app/components/DevServerView.css     | 100 ++++-
 .../dashboard/app/components/DevServerView.tsx     | 449 +++++++++++++++------
 .../__tests__/DevServerView.mobile.test.tsx        |  17 +-
 .../__tests__/DevServerView.preview.test.tsx       | 148 +++++++
 6 files changed, 603 insertions(+), 119 deletions(-)

Fusion-Task-Id: FN-6939

Fusion-Task-Lineage: 745ea56d-16bf-4246-bfe7-0461754466d9
2026-06-23 16:47:38 -07:00
gsxdsm
7b60539006 FN-6953: honor unsaved ntfy config in tests
Let ntfy test notifications use the current Settings form values before they are saved.

- Send unsaved ntfy enabled/topic/server/token values from all Settings ntfy test buttons.
- Resolve request-scoped ntfy config on the backend for general, message, and room test notifications.
- Publish message and room ntfy tests directly with the resolved config instead of the active persisted notification service.
- Cover unsaved ntfy test behavior across Settings UI and API routes.

Files changed:
 .changeset/fn-6953-ntfy-test-unsaved-config.md     |   5 +
 .../dashboard/app/components/SettingsModal.css     |   5 +-
 .../dashboard/app/components/SettingsModal.tsx     |  21 +-
 .../components/__tests__/SettingsModal.test.tsx    |  60 +++-
 .../src/__tests__/routes-settings.test.ts          | 181 ++++++++---
 .../src/routes/register-settings-memory-routes.ts  | 355 +++++++++------------
 6 files changed, 380 insertions(+), 247 deletions(-)

Fusion-Task-Id: FN-6953

Fusion-Task-Lineage: 90557168-3b11-4ab4-b5c1-4261225600ba
2026-06-23 16:47:38 -07:00
gsxdsm
2954b07965 Merge pull request #1711 from Runfusion/gsxdsm/u0-merger-unification
refactor(merger): unify on runAiMerge as the sole merge path (U0 / workspace Phase 0)
2026-06-23 16:41:41 -07:00
gsxdsm
9822fbd6a8 Merge remote-tracking branch 'origin/main' into conflict-resolution-1718
# Conflicts:
#	packages/engine/src/__tests__/executor-recovery.test.ts
#	packages/engine/src/agent-tools.ts
#	packages/engine/src/executor.ts
#	packages/engine/src/merger-ai.ts
#	packages/engine/src/project-engine.ts
#	packages/engine/src/run-audit.ts
#	packages/engine/src/self-healing.ts
#	packages/engine/src/worktree-acquisition.ts
2026-06-23 16:23:28 -07:00
gsxdsm
0dea25061d Merge remote-tracking branch 'origin/main' into conflict-resolution-1717
# Conflicts:
#	packages/engine/src/__tests__/executor-recovery.test.ts
#	packages/engine/src/agent-tools.ts
#	packages/engine/src/executor.ts
#	packages/engine/src/merger-ai.ts
#	packages/engine/src/project-engine.ts
#	packages/engine/src/worktree-acquisition.ts
2026-06-23 16:12:10 -07:00
gsxdsm
941343f483 Merge remote-tracking branch 'origin/main' into conflict-resolution-1714
# Conflicts:
#	packages/engine/src/__tests__/executor-recovery.test.ts
#	packages/engine/src/agent-tools.ts
#	packages/engine/src/executor.ts
#	packages/engine/src/worktree-acquisition.ts
2026-06-23 16:01:38 -07:00
gsxdsm
dcee0d3172 Merge remote-tracking branch 'origin/main' into conflict-resolution-1713
# Conflicts:
#	packages/engine/src/__tests__/executor-recovery.test.ts
#	packages/engine/src/agent-tools.ts
#	packages/engine/src/executor.ts
#	packages/engine/src/worktree-acquisition.ts
2026-06-23 15:56:06 -07:00
gsxdsm
4faadd4f75 Merge remote-tracking branch 'origin/main' into conflict-resolution-1712
# Conflicts:
#	packages/dashboard/app/components/WorkflowNodeEditor.tsx
#	packages/engine/src/__tests__/executor-fast-mode-workflows.test.ts
2026-06-23 15:44:52 -07:00
gsxdsm
a8abecd043 Merge remote-tracking branch 'origin/main' into conflict-resolution-1711
# Conflicts:
#	packages/engine/src/executor.ts
2026-06-23 15:37:29 -07:00
gsxdsm
905a877954 Merge pull request #1710 from MichaelHoughtonDeBox/feat/workspace-multi-repo
Workspace mode: open a folder of git repos as one project (foundation + design Q)
2026-06-23 15:08:15 -07:00
gsxdsm
bac7049822 chore(release): v0.46.0
Version bump via changesets.
2026-06-23 12:27:50 -07:00
gsxdsm
41f3b045ea FN-6920: add LOC backfill controls to productivity
Adds an explicit Productivity-area workflow for previewing and applying historical LOC diff-stat backfills.\n\n- Add preview and confirmed apply actions for commit-association LOC backfills in Command Center Productivity.\n- Surface dry-run/applied report counts, pending, and error states while preserving unavailable LOC sentinels.\n- Reuse tokenized responsive backfill styling and add localized strings, docs, regression coverage, and a patch changeset.\n\nFiles changed:\n .changeset/loc-backfill-command-center.md          |   5 +\n docs/dashboard-guide.md                            |   2 +-\n .../components/command-center/CommandCenter.css    |  21 ++-\n .../command-center/areas/ProductivityArea.tsx      | 142 ++++++++++++++-\n .../command-center/areas/__tests__/areas.test.tsx  | 200 +++++++++++++++++++++\n packages/i18n/locales/en/app.json                  |  15 ++\n packages/i18n/locales/es/app.json                  |  15 ++\n packages/i18n/locales/fr/app.json                  |  15 ++\n packages/i18n/locales/ko/app.json                  |  15 ++\n packages/i18n/locales/zh-CN/app.json               |  15 ++\n packages/i18n/locales/zh-TW/app.json               |  15 ++\n packages/i18n/src/resources.d.ts                   |  29 ++-\n 12 files changed, 476 insertions(+), 13 deletions(-)

Fusion-Task-Id: FN-6920

Fusion-Task-Lineage: 2e81ec44-46f3-49a9-ba4a-a4a72a38a6e8
2026-06-23 12:17:04 -07:00
gsxdsm
736ec6d175 FN-6957: prevent stale mailbox deep links from reselecting messages
Treat mailbox deep links as one-shot navigation so mobile users can switch messages without stale URL state restoring the prior selection.

- consume the current mailbox deep link before explicit user navigation, compose, delete, reply, and approval actions
- prevent repeated deep-link highlighting and clear stale selections when switching agent mailboxes or subtabs
- add regression coverage for modal and full mailbox views, plus a published CLI patch changeset

Files changed:
 .changeset/fn-6957-mailbox-stale-deeplink.md       |   5 +
 packages/dashboard/app/components/MailboxModal.tsx |  56 ++++++---
 packages/dashboard/app/components/MailboxView.tsx  |  60 +++++++---
 .../app/components/__tests__/MailboxModal.test.tsx |  76 ++++++++++++
 .../app/components/__tests__/MailboxView.test.tsx  | 130 +++++++++++++++++++++
 5 files changed, 293 insertions(+), 34 deletions(-)

Fusion-Task-Id: FN-6957

Fusion-Task-Lineage: 2a85c8d1-2163-434a-987f-2e842b847651
2026-06-23 12:17:04 -07:00
gsxdsm
d6a94d0062 Merge pull request #1735 from Runfusion/feature/memory-fix
[codex] Reduce dashboard SSE churn and TUI heap growth
2026-06-23 12:16:43 -07:00
gsxdsm
4704e58ed8 fix(cli): drain TUI performance entries 2026-06-23 12:10:40 -07:00
gsxdsm
61d2a1a609 fix(engine): bound one-shot CLI output capture 2026-06-23 11:48:38 -07:00
gsxdsm
a553459b76 FN-6926: target task worktrees in dev server
Adds an executing-task picker so dev server previews can run against in-progress task worktrees.

- Pass live task data into the Dev Server view from the main app and overflow dock.
- List in-progress tasks with worktrees, show the selected task descriptor, and prefer its worktree when starting the server.
- Fall back to the legacy start endpoint when an explicit cwd override is needed.
- Cover the picker, descriptor, cwd selection, restart guidance, and empty-state behavior with dashboard tests.
- Document task-targeted dev server previews in the dashboard guide.

Files changed:
 docs/dashboard-guide.md                            |   1 +
 packages/dashboard/app/App.tsx                     |   2 +-
 .../dashboard/app/components/DevServerView.css     |  61 +++++++++
 .../dashboard/app/components/DevServerView.tsx     |  85 +++++++++++-
 .../components/__tests__/DevServerView.test.tsx    | 146 +++++++++++++++++++++
 .../app/components/overflowViewRegistry.tsx        |   2 +-
 packages/dashboard/app/hooks/useDevServer.ts       |  13 +-
 7 files changed, 303 insertions(+), 7 deletions(-)

Fusion-Task-Id: FN-6926

Fusion-Task-Lineage: beb05a39-c9f1-4017-afcd-a8d361ef232c
2026-06-23 11:26:51 -07:00
gsxdsm
f6e9deb13f FN-6958: stop auto-focusing Planning Mode composer
Planning Mode now opens without forcing focus into the initial text area.\n\n- Remove the open-time autofocus effect while preserving explicit textarea focus behavior.\n- Cover mobile, embedded desktop, and initialPlan handoff behavior with regression tests.\n- Add a patch changeset for the published CLI package.\n\nFiles changed:\n .changeset/FN-6958-planning-focus.md               |  5 ++\n .../dashboard/app/components/PlanningModeModal.tsx | 10 ++--\n .../__tests__/PlanningModeModal.initial.test.tsx   | 69 ++++++++++++++++++++++\n 3 files changed, 78 insertions(+), 6 deletions(-)

Fusion-Task-Id: FN-6958

Fusion-Task-Lineage: df030f3f-c9ca-4420-8618-bc3ec44d6e12
2026-06-23 11:26:50 -07:00
gsxdsm
54be35f537 Merge pull request #1732 from Runfusion/feature/memory-fix
[codex] Reduce dashboard SSE keepalive churn
2026-06-23 11:26:03 -07:00
gsxdsm
fade815c85 fix(dashboard): reduce SSE keepalive churn 2026-06-23 10:43:14 -07:00
gsxdsm
fe536b2af8 FN-6954: reconcile stale parked task assignments
Reconcile agent/task drift when durable agents remain linked to queued tasks without live execution proof.

- clear stale Agent.taskId links for parked todo/triage tasks while preserving task leases and queue state
- report stale parked assignments as active/no-live-run in Reports Health Check before reconciliation completes
- add scheduler and self-healing coverage for queued lease drift, overlap starvation, and audit events
- document the reconciliation behavior and add a published package patch changeset

Files changed:
 .changeset/fn-6954-agent-task-state-drift.md       |   5 +
 docs/architecture.md                               |   2 +
 .../src/__tests__/heartbeat-executor.test.ts       |  68 ++++++++++++
 .../__tests__/scheduler-overlap-starvation.test.ts |  68 +++++++++++-
 .../self-healing-agent-link-drift.test.ts          |  97 ++++++++++++++++-
 .../engine/src/__tests__/task-agent-sync.test.ts   |  33 +++++-
 packages/engine/src/agent-heartbeat.ts             | 121 +++++++++++++++++++--
 packages/engine/src/run-audit.ts                   |   5 +
 packages/engine/src/runtimes/in-process-runtime.ts |   1 +
 packages/engine/src/scheduler.ts                   |  24 +++-
 packages/engine/src/self-healing.ts                | 102 ++++++++++++++---
 packages/engine/src/task-agent-sync.ts             |  65 ++++++++++-
 12 files changed, 555 insertions(+), 36 deletions(-)

Fusion-Task-Id: FN-6954
Fusion-Task-Lineage: 24b8a2eb-5a33-4539-ab64-ae2bbfc2d195
2026-06-23 10:30:50 -07:00
gsxdsm
81f43e0e89 Merge pull request #1730 from Runfusion/feature/memory-fix
[codex] Dispose completed spawned child sessions
2026-06-23 10:29:30 -07:00
gsxdsm
a46800128a Address PR review feedback (#1730)
- Await async spawned child session disposal

- Use own-key iteration for structured tool result previews

- Add FNXC requirement comments for new regression assertions
2026-06-23 09:57:49 -07:00
gsxdsm
e59b1fa07d Merge pull request #1726 from plarson/fix/triage-spec-review-fallback
fix(triage): pass fallback model settings to spec review
2026-06-23 09:52:58 -07:00
gsxdsm
36b7ad90fd fix(engine): bound structured tool result log previews 2026-06-23 09:19:32 -07:00
gsxdsm
b02fb62c04 fix(core): avoid loading artifact content in registry lists 2026-06-23 09:16:57 -07:00
gsxdsm
d06e31693e fix(command-center): keep Recharts cards from blanking
Measure Command Center Recharts wrappers directly and keep usable fallback dimensions until lazy dashboard cards report a drawable box. This restores line charts such as Daily activity, Tokens trend, and Activity trend, plus model-share pies that used the same sizing path.
2026-06-23 09:12:44 -07:00