Commit Graph

9821 Commits

Author SHA1 Message Date
gsxdsm
d9efea9e27 docs(workspace): capture Task-field persistence gotcha learning
Adds a docs/solutions learning: a Task field is silently dropped on persist
unless it has a SQLite column + defineTaskColumn + rowToTask mapping (applyTaskPatch
writes the DB-round-tripped task back over task.json). Plus a CONCEPTS.md entry for
the active-session lease (the path-keyed exclusivity/liveness registry whose key
choice caused the concurrent-workspace collision).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-24 15:35:49 -07:00
gsxdsm
0db585fd17 Merge branch 'main' into feat/dashboard-workspace-wizard 2026-06-24 15:15:54 -07:00
gsxdsm
f06281961e fix(workspace): persist workspaceWorktrees and isolate concurrent session leases
Multiworkspace tasks could not complete due to two independent bugs:

1. task.workspaceWorktrees had no SQLite column / rowToTask mapping, so
   fn_acquire_repo_worktree's updateTask write was dropped on every persist
   (applyTaskPatch writes the DB-round-tripped task back to task.json). Every
   later getTask returned undefined, so fn_task_done's scope verifier read {}
   and blocked with "acquired no sub-repo worktrees", and isWorkspaceTask()
   consumers misfired. Persist it mirroring mergeDetails (schema column + v129
   migration + db-migrate + defineTaskColumn + TaskRow + rowToTask).

2. In workspace mode every task ran rooted at the shared browse-only root, and
   setActiveSession registered that path keyed only by path — so a second
   concurrent workspace task was rejected by the foreign-task guard
   ("active-session path ... is held by ..."). Give each task a task-scoped
   synthetic session key (sessionRegistryPath), applied at all register and
   unregister sites; the in-memory worktree Set still holds the real root.

Regression tests assert the persistence invariant across getTask/listTasks/
store-reopen and concurrent session registration across all three session
surfaces; both verified to fail without the fix.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-24 15:13:43 -07:00
gsxdsm
be2866ee66 feat(git-manager): multi-repo workspace support with repo selector
- Add GET /api/git/workspace-repos endpoint returning sub-repo list
- Add resolveGitDir() helper: resolves repoPath query param to sub-repo dir
- Update all 34 git endpoints to use resolveGitDir for workspace targeting
- Add repoPath param to 30+ frontend git API functions
- GitManagerModal: auto-detect workspace repos on mount, show repo selector
  dropdown at top of sidebar, pass selected repo to all git API calls
- Auto-select first repo when workspace mode is detected
- Repo change triggers data refetch via gitRepoPath dependency
2026-06-24 15:02:12 -07:00
gsxdsm
4006b303de feat(dashboard): workspace detection + task prefix in project import wizard (#1746)
## Problem

After PR #1741 merged the core workspace mode defaults and prefix
derivation, the dashboard project import wizard (SetupWizardModal) still
had no workspace detection or task prefix field. Users importing a
multi-repo project (e.g. a directory containing `openvide/` and
`swarmclaw/` as separate git repos) saw no workspace mode option and no
prefix configuration.

## Changes

### New API endpoint: `POST /api/projects/detect-workspace`
- Probes a directory for git sub-repos using `detectWorkspaceRepos` from
`@fusion/core`
- Returns `{ repos: string[], isWorkspace: boolean }`
- Excludes `node_modules`, `.fusion`, `.git`, `.pi` from detection

### Modified `POST /api/projects` registration route
- Accepts `workspaceMode` and `taskPrefix` from the client
- When `workspaceMode: true`, detects and persists sub-repos to
`workspace.json` and sets `workspaceMode: true` in config.json
- When unspecified, auto-detects sub-repos and applies workspace mode if
found
- Task prefix uses client-provided value or falls back to
`suggestTaskPrefix(name)`

### SetupWizardModal UI
- **Workspace mode checkbox**: Auto-detects sub-repos when a directory
path is entered via `POST /api/projects/detect-workspace`. Shows a
pre-checked "Workspace mode (multi-repo)" checkbox listing detected
repos when sub-repos are found
- **Task prefix field**: Auto-derived from project name, editable,
capped at 5 chars. Shown alongside the workspace checkbox

### Review fixes (from PR #1741 round 3)
- Aligned dashboard settings regex from `{1,10}` to `{1,5}` to match CLI
cap
- Fixed `distributed-task-id.ts` fallback from `"KB"` to `"FN"` (3
occurrences)
- Moved CLI `taskPrefix`/`defaultWorkflowId` persistence outside
interactive-only block
- Wrapped both CLI `TaskStore` lifecycles in `try/finally` to guarantee
`close()` on error

## Testing
- `pnpm typecheck` — pass
- `pnpm lint` — pass
- `pnpm test:gate` — 313/313 pass
- `vitest run git-repository.test.ts` — 8/8 pass
- Verified `detectWorkspaceRepos` returns `["openvide", "swarmclaw"]` on
`/Users/eclipxe/Projects/multiclaw`

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

---

<a href="https://stagereview.app/Runfusion/Fusion/pull/1746">
  <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 workspace detection during project setup, including sub-repo
discovery and a workspace mode toggle when applicable.
* Added task prefix support during setup/registration, with
auto-suggestions derived from the project name and persisted
preferences.
* Exposed a workspace-detect API used by the setup wizard to guide
selection.

* **Bug Fixes**
* Improved setup registration cleanup to reliably close background
resources.
* Updated the legacy task ID prefix fallback when configuration is
missing or unreadable.
  * Tightened task prefix validation length limits in settings.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2026-06-24 14:32:00 -07:00
gsxdsm
b680948d8c Address PR review feedback (#1746)
- Fix race condition: add request ID guard for stale workspace detection responses
- Guard workspaceMode:true: only persist when repos.length > 0
- Add server-side taskPrefix validation (/^[A-Z]{1,5}$/)
- Move store.init() inside try/finally in both CLI TaskStore lifecycles
- Accept 1-character prefixes in CLI prompt (was requiring >= 2)
- Fix workspaceMode passing: forward false explicitly (was coerced to undefined)
- Fix 3rd KB→FN fallback in distributed-task-id.ts catch block
- Add try/finally to dashboard TaskStore in register-project-routes
- Gate workspace detection on existing-directory mode only (skip clone mode)
2026-06-24 14:24:07 -07:00
gsxdsm
46d0c4a1f6 Address PR review feedback round 3 (#1741)
- Align dashboard prefix validation to 1-5 chars (was 1-10) matching CLI cap
- Fix distributed-task-id.ts fallback from KB to FN (3 occurrences)
- Move taskPrefix/defaultWorkflowId persistence outside interactive-only block
  so non-interactive CLI registration also gets defaults
- Wrap both TaskStore lifecycles in try/finally to guarantee close() on error
2026-06-24 14:08:32 -07:00
gsxdsm
9e7c85d221 feat(dashboard): workspace detection + task prefix in import wizard
- Add POST /api/projects/detect-workspace endpoint for sub-repo scanning
- Modify POST /api/projects to accept workspaceMode + taskPrefix params
- SetupWizardModal: auto-detect sub-repos when path is entered, show
  workspace mode checkbox with detected repo count, add task prefix field
  auto-derived from project name
- Wire workspaceMode and taskPrefix through registration API call
2026-06-24 14:08:32 -07:00
gsxdsm
c2030220f5 feat(workspace): workspace mode toggle, derived task prefix, default coding workflow (#1741)
## Problem

After PR #1739 merged the core workspace auto-detection fix, additional
workspace UX was needed:
1. Onboarding should interactively confirm workspace mode and let the
user pick a task prefix and default workflow.
2. Task prefix should be derived from the project name (2-4 chars
uppercase) instead of a hardcoded constant.
3. Default fallback prefix should be `FN` (matching the product name),
not the legacy `KB`.
4. Dashboard registration should also auto-derive prefix and set the
coding workflow.
5. Default workflow should be `builtin:coding`.

## Changes

### `packages/cli/src/project-resolver.ts`
- **Interactive workspace confirmation**: When sub-repos are detected,
ask the user to confirm workspace mode instead of auto-applying
(non-interactive/dashboard still auto-applies).
- **`suggestTaskPrefix(projectName)`**: Derives a 2-4 char prefix from
the project name (first letters of words, or first chars of a single
word).
- **Onboarding prefix prompt**: Shows the suggested prefix and lets the
user confirm or override.
- **Default coding workflow**: Sets `defaultWorkflowId:
"builtin:coding"` for new projects.

### `packages/core/src/settings-schema.ts`
- `DEFAULT_PROJECT_SETTINGS.workspaceMode`: changed from `false` to
`undefined` so `TaskStore.init()` does not write `workspaceMode: false`
to config.json before auto-detection runs (which would block it via
`isWorkspaceModeExplicitlyDisabled`).
- `DEFAULT_PROJECT_SETTINGS.taskPrefix`: changed to `undefined` (falls
back to `"FN"` in store).
- `DEFAULT_PROJECT_SETTINGS.defaultWorkflowId`: set to
`"builtin:coding"`.

### `packages/core/src/store.ts`
- Task prefix fallback changed from `"KB"` to `"FN"`.

### `packages/dashboard/src/routes/register-project-routes.ts`
- Auto-derives task prefix from project name for dashboard
registrations.
- Sets `defaultWorkflowId: "builtin:coding"` for new projects.

## Testing
- `pnpm typecheck` — pass
- `pnpm lint` — pass
- `vitest run git-repository.test.ts` — 8/8 pass

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

---

<a href="https://stagereview.app/Runfusion/Fusion/pull/1741">
  <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**
* Project onboarding now proposes a task prefix derived from the project
name, and stores it during setup (interactive onboarding and new
registrations).
* New projects start with the default coding workflow enabled to improve
first-time experience.
* **Bug Fixes**
* Improved distributed task ID prefix fallback when no custom prefix is
configured (now uses `FN`).
* **Chores**
* Updated default project settings so `taskPrefix` and `workspaceMode`
begin unset, aligning with onboarding behavior.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2026-06-24 11:45:33 -07:00
gsxdsm
b317a39d80 Cap interactive prefix input to 5 chars (#1741) 2026-06-24 11:39:40 -07:00
gsxdsm
06adc190ce Add max-length cap (10) to interactive prefix input (#1741)
CodeRabbit: suggestTaskPrefix caps at 4 chars, but user input was uncapped.
Align with the dashboard validation regex (1-10 uppercase letters).
2026-06-24 11:34:38 -07:00
gsxdsm
aae76cecc3 Address PR review feedback (#1741)
- Close first TaskStore before creating second in interactive registration (P1)
- Revert defaultWorkflowId default to undefined; set explicitly in onboarding only (P1)
- Add alpha-only filter + 2-char min to interactive prefix input (P2)
- Move suggestTaskPrefix to @fusion/core, share between CLI and dashboard (P2)
- Fix suggestTaskPrefix JSDoc to match implementation (P2)
2026-06-24 10:45:21 -07:00
gsxdsm
9a7c0c6154 fix: fallback task prefix to FN (was KB) when unset
The hardcoded fallback prefix in store.ts was 'KB' (legacy name). Changed
to 'FN' to match the product name and dashboard placeholder.
2026-06-24 10:31:06 -07:00
gsxdsm
800f845e15 feat(workspace): fix auto-detection, derive prefix from name, default coding workflow
- Fix workspace detection: change workspaceMode default from false to
  undefined so isWorkspaceModeExplicitlyDisabled no longer blocks
  auto-detection on fresh projects (config.json was being written with
  workspaceMode:false during store.init(), causing the guard to skip
  detection before it ever ran)
- Derive task prefix from project name (first 2-4 chars) instead of
  hardcoded 'FN' as the suggested default
- Default workflow is now builtin:coding instead of undefined
- CLI registerProjectInteractive: onboarding prompt for task prefix
  confirmation after project name
- Dashboard POST /api/projects: auto-derive prefix and set default
  workflow for new registrations
2026-06-24 10:31:06 -07:00
gsxdsm
e1f419ae17 fix(workspace): detect sub-repos when workspace.json is missing (#1739)
## Problem

The previous fix (#1738) only checked `loadWorkspaceConfig` to skip `git
init` on workspace-mode roots. However, the dashboard `POST
/api/projects` and `fn project add` routes never create `workspace.json`
(only the CLI interactive setup in `registerProjectInteractive` does).
So re-adding a workspace project through the dashboard still triggered
`git init` because the guard saw no `workspace.json`, creating a stray
`.git` at the workspace root.

## Fix

Add `detectWorkspaceRepos` as a fallback in
`ensureGitRepositoryForProjectPath`: after `loadWorkspaceConfig` and
`isInsideGitWorkTree` both miss, probe for git sub-repos. If found,
persist `workspace.json` and return `"existing"` without running `git
init`. This covers all registration surfaces.

## Files Changed

- `packages/core/src/git-repository.ts` — `detectWorkspaceRepos`
fallback + `saveWorkspaceConfig` + FNXC comment
- `packages/core/src/__tests__/git-repository.test.ts` — regression test
verifying sub-repo detection skips `git init` and persists
`workspace.json`

## Testing

- `pnpm typecheck` (`@fusion/core`) — pass
- `pnpm lint` — pass
- `vitest run git-repository.test.ts` — 6/6 pass (5 existing + 1 new)

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

---

<a href="https://stagereview.app/Runfusion/Fusion/pull/1739">
  <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 a “Workspace mode (multi-repo)” checkbox in the dashboard to
enable/disable workspace behavior.
* Updated CLI interactive mode to prompt for using workspace mode;
non-interactive mode continues to auto-detect sub-repositories.
* **Bug Fixes**
* Improved workspace auto-detection when workspace config is missing,
including persisting detected repositories.
* Workspace mode now skips creating a root `.git` when sub-repositories
are detected.
* When workspace mode is explicitly disabled, auto-detection is skipped;
disabling also removes the workspace config.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2026-06-24 10:10:12 -07:00
gsxdsm
c1b5be7d69 fix(workspace): write config.json before workspace.json, validate settings object
Address PR #1739 review round 3:

- Major (coderabbit): Reorder writes so setWorkspaceModeInConfig runs
  before saveWorkspaceConfig. If the config write fails, no stale
  workspace.json is left behind.
- Major (coderabbit): setWorkspaceModeInConfig only treats ENOENT as
  empty config (not parse errors or permission errors). Validates
  settings is a plain object before merging to prevent clobbering.
2026-06-24 09:02:02 -07:00
gsxdsm
11ffca1611 fix(workspace): persist workspaceMode:true in config.json, let save errors propagate
Address PR #1739 review round 2:

- P1 (greptile): Auto-detection fallback now sets workspaceMode: true in
  config.json so the dashboard toggle reflects the actual state.
- Major (coderabbit): Let saveWorkspaceConfig errors propagate instead of
  silently returning 'existing' when the write fails. A failed write would
  leave the project with no git repo and no workspace config.
2026-06-24 08:36:55 -07:00
gsxdsm
42342eff03 fix(workspace): respect explicit workspaceMode:false, improve exclusion test
Address PR #1739 review feedback:

- P1 (greptile): When workspaceMode is explicitly false in config.json,
  skip the auto-detection fallback so toggling workspace mode off via the
  dashboard has a lasting effect (was being re-enabled on next registration).
- CodeRabbit: node_modules exclusion test now includes a real sibling
  sub-repo to prove the exclusion is the gate, not just absence of
  detection.
- Add test for workspaceMode:false config.json guard.
2026-06-24 08:16:36 -07:00
gsxdsm
9aaf911735 feat(workspace): add per-project workspaceMode setting with interactive confirmation
Add workspaceMode as a first-class ProjectSettings boolean that controls
whether the project root is treated as a workspace parent (multi-repo)
or a single git repo.

- ProjectSettings type + DEFAULT_PROJECT_SETTINGS: workspaceMode?: boolean
- CLI registerProjectInteractive: when sub-repos are detected, ask the
  user to confirm workspace mode instead of auto-applying
- TaskStore.updateSettings: when workspaceMode is toggled on, detect
  sub-repos and persist workspace.json; when toggled off, remove it
- Dashboard SettingsModal GeneralSection: workspace mode toggle checkbox

This lets users change workspace mode per-project at any time via the
dashboard Settings or PUT /settings API.
2026-06-24 00:45:24 -07:00
gsxdsm
ff155b9df7 fix(workspace): exclude node_modules from detection, best-effort save
Address PR #1739 review feedback:

- P1: Exclude node_modules, .fusion, .pi from detectWorkspaceRepos so
  packages installed from git sources don't produce false-positive
  workspace members.
- P2: Wrap saveWorkspaceConfig in try/catch so a write failure (permissions,
  disk full) doesn't fail the current registration.
- Nitpick: Thread runner/timeout through detectWorkspaceRepos so custom-runner
  callers are consistent across all code paths.
2026-06-24 00:26:31 -07:00
gsxdsm
cab375a6f8 fix(workspace): detect sub-repos when workspace.json is missing
The initial fix only checked loadWorkspaceConfig, but the dashboard
POST /api/projects and `fn project add` routes never create workspace.json
(only registerProjectInteractive does). So re-adding a workspace project
through the dashboard still triggered git init because the guard saw no
workspace.json.

Add detectWorkspaceRepos as a fallback: after loadWorkspaceConfig and
isInsideGitWorkTree both miss, probe for git sub-repos. If found, persist
workspace.json and return 'existing' without running git init. This covers
all registration surfaces.
2026-06-24 00:16:37 -07:00
gsxdsm
b9821eebd7 FN-6965: stack list-pane chat agent headers
Stack List View task-chat agent headers above their output in the split detail pane while keeping desktop chat layouts unchanged.

- Add a split-pane-specific CSS override that stacks task chat groups in List View only.
- Keep compact header sizing safe without affecting modal, board, expanded, or popped-out chat layouts.
- Cover List View populated, loading, empty, and CSS host-scope behavior in TaskChatTab tests.
- Add a patch changeset for the published Fusion package.

Files changed:
 .changeset/fn-6965-list-pane-chat-layout.md        |  5 ++
 packages/dashboard/app/components/TaskChatTab.css  | 12 +++
 .../app/components/__tests__/TaskChatTab.test.tsx  | 90 ++++++++++++++++++++++
 3 files changed, 107 insertions(+)

Fusion-Task-Id: FN-6965

Fusion-Task-Lineage: 8107ae8c-c25e-497e-8506-4c77e3e083d3
2026-06-23 23:35:33 -07:00
gsxdsm
1f0a48582d FN-6940: expose New Task quick-add actions
Expose the regular New Task dialog's primary creation and quick-add controls in one visible action row.

- Move the Create Task submit affordance into TaskForm's create-mode action cluster.
- Promote GitHub tracking, workflow, model, node, fast mode, attach, priority, plan, and subtask controls as inline quick-add buttons.
- Add responsive spacing and mobile touch-target coverage for the expanded action row.
- Extend NewTaskModal and TaskForm tests around the promoted controls and submission flow.

Files changed:
 packages/dashboard/app/components/NewTaskModal.css |  12 ++
 packages/dashboard/app/components/NewTaskModal.tsx |  10 +-
 packages/dashboard/app/components/TaskForm.tsx     | 110 +++++++++++++++-
 .../app/components/__tests__/NewTaskModal.test.tsx | 139 ++++++++++++++++++---
 .../app/components/__tests__/TaskForm.test.tsx     |   3 +
 .../__tests__/core-modals-mobile.test.tsx          |   8 +-
 6 files changed, 250 insertions(+), 32 deletions(-)

Fusion-Task-Id: FN-6940

Fusion-Task-Lineage: 8329c432-5e01-40aa-a3da-c13754a74393
2026-06-23 23:35:33 -07:00
gsxdsm
038ac3060b FN-6963: disable tool output log details by default
Agent logs now keep tool timeline rows while requiring an explicit opt-in to persist verbose tool payloads.

- Default persistAgentToolOutput to false in global settings and direct AgentLogger construction.
- Update the settings UI, API expectations, documentation, and changeset to describe opt-in tool payload persistence.
- Adjust engine and dashboard tests for the new default-off behavior while preserving explicit opt-in coverage.

Files changed:
 .changeset/FN-6963-tool-output-default-off.md      |  5 ++++
 docs/settings-reference.md                         |  2 +-
 packages/core/src/settings-schema.ts               |  6 ++++-
 .../components/__tests__/SettingsModal.test.tsx    | 22 +++++++++++++---
 .../settings/sections/GlobalGeneralSection.tsx     |  2 +-
 .../src/__tests__/routes-settings.test.ts          |  4 +--
 packages/engine/src/__tests__/agent-logger.test.ts | 30 ++++++++++++++++++----
 .../src/__tests__/heartbeat-executor.test.ts       |  4 +--
 .../src/__tests__/merger-merge-details.test.ts     |  2 +-
 .../src/__tests__/merger-verification.test.ts      |  2 +-
 .../src/__tests__/step-session-executor.test.ts    |  4 +--
 packages/engine/src/__tests__/triage.test.ts       |  2 +-
 packages/engine/src/agent-logger.ts                |  8 ++++--
 13 files changed, 71 insertions(+), 22 deletions(-)

Fusion-Task-Id: FN-6963

Fusion-Task-Lineage: 7db32871-f539-4a27-a324-02c55ce5bd04
2026-06-23 23:35:33 -07:00
gsxdsm
cf2f3ba5e4 FN-6956: close task detail immediately after confirmed deletes
Close task detail surfaces as soon as delete confirmations finish while preserving async result toasts.

- Move Task Detail close requests ahead of delete request settlement and reuse them for conflict retry paths.
- Cover dialog, mobile-header, embedded host, retry, and cancelled-confirmation delete behavior in tests.
- Document the immediate-close delete behavior and add a patch changeset.

Files changed:
 .changeset/fn-6956-immediate-delete-close.md       |   5 +
 docs/dashboard-guide.md                            |   1 +
 .../dashboard/app/components/TaskDetailModal.tsx   |  20 ++-
 .../components/__tests__/TaskDetailModal.test.tsx  | 140 ++++++++++++++++++++-
 4 files changed, 160 insertions(+), 6 deletions(-)

Fusion-Task-Id: FN-6956

Fusion-Task-Lineage: 02f70c0c-c807-4a22-945e-646acbfe8def
2026-06-23 23:35:32 -07:00
gsxdsm
92f23f81a8 fix(workspace): skip git init for workspace-mode project roots (#1738)
## Problem

`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 sets session cwd to the workspace root (browse-only mode)
- `git rev-parse --abbrev-ref HEAD` fails with `fatal: ambiguous
argument 'HEAD'` on the unborn HEAD
- All workspace task execution breaks (`fn_task_done refused:
wrong_branch`, `expected swarmclaw/ repository is absent`)

## Fix

Add an early-return guard in `ensureGitRepositoryForProjectPath` that
checks `loadWorkspaceConfig(projectPath)` before any git operation. When
`.fusion/workspace.json` is present (workspace mode), the function
returns `"existing"` immediately, keeping the workspace root non-git as
intended by the workspace execution contract.

## Files Changed

- `packages/core/src/git-repository.ts` — workspace-mode early-return
guard + FNXC:Workspace comment
- `packages/core/src/__tests__/git-repository.test.ts` — regression test
verifying no `.git` is created at workspace root

## Testing

- `pnpm typecheck` — pass
- `pnpm lint` — pass
- `pnpm test` (affected `git-repository.test.ts`) — 5/5 pass (4 existing
+ 1 new)

## Remediation for existing broken workspaces

Users with an already-registered broken workspace project should remove
the stray repo:
```bash
rm -rf <workspace-root>/.git
```

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

---

<a href="https://stagereview.app/Runfusion/Fusion/pull/1738">
  <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

* **Bug Fixes**
* Improved project setup so workspace-root projects are no longer
initialized as Git repositories when workspace configuration is present.
* Prevents unexpected `.git` creation at the workspace root and
preserves existing workspace behavior.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2026-06-23 23:34:56 -07:00
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