Commit Graph

9883 Commits

Author SHA1 Message Date
gsxdsm
bc56ab287e fix: align tests with recent source changes on main
Fix 6 failing tests caused by intentional source changes that landed
without updating dependent test assertions:

- Core test-project: taskPrefix default changed from "FN" to undefined
  (commit 800f845e1, derived from project name at runtime)
- Dashboard ScriptsModal.css: replace banned --text-primary with --text
- CLI package-config: update expected pi dep version ^0.79.1 -> ^0.79.9
- CLI skill-sync: document 4 new engine tools in engine-tools.md
- CLI version: update expected release:version script to include
  run-ci-distill.mjs
- CLI bundled-plugin-freshness: rebuild stale dist directories
2026-06-25 00:13:29 -07:00
gsxdsm
f9816799a0 FN-7000: test unified CLI merge entrypoints
Strengthen CLI merge tests around the unified runAiMerge path.\n\n- Mock runAiMerge separately from deprecated aiMergeTask in CLI task and dashboard tests.\n- Assert task merge uses runAiMerge without falling back to workspace landing or aiMergeTask.\n- Verify dashboard manual merge logging streams through runAiMerge and restores spies safely.\n\nFiles changed:\n .../cli/src/commands/__tests__/dashboard.test.ts   | 51 +++++++++++++---------\n packages/cli/src/commands/__tests__/task.test.ts   | 30 +++++++++----\n 2 files changed, 52 insertions(+), 29 deletions(-)

Fusion-Task-Id: FN-7000

Fusion-Task-Lineage: 96080841-c813-4a9e-97bd-a41aba60089f
2026-06-24 23:32:52 -07:00
gsxdsm
bd5a779423 FN-6982: remove task chat guidance copy
Remove the task Chat composer guidance line and rely on placeholders for task state cues.

- Drop the idle/guidance text block above the task Chat entry box.
- Remove obsolete localized guidance strings and generated resource types.
- Update dashboard docs, regression coverage, and release notes for the simplified composer.

Files changed:
 .changeset/fn-6982-task-chat-guidance.md           |   7 ++
 docs/dashboard-guide.md                            |   2 +-
 packages/dashboard/app/components/TaskChatTab.css  |  10 --
 packages/dashboard/app/components/TaskChatTab.tsx  |  83 +----------------
 .../app/components/__tests__/App.test.tsx          |  42 ++++-----
 .../PlanningModeModal.ui-interactions.test.tsx     |   2 +-
 .../app/components/__tests__/TaskChatTab.test.tsx  | 102 +++++++++++----------
 packages/i18n/locales/en/app.json                  |   2 -
 packages/i18n/locales/es/app.json                  |   2 -
 packages/i18n/locales/fr/app.json                  |   2 -
 packages/i18n/locales/ko/app.json                  |   2 -
 packages/i18n/locales/zh-CN/app.json               |   2 -
 packages/i18n/locales/zh-TW/app.json               |   2 -
 packages/i18n/src/resources.d.ts                   |   2 -
 14 files changed, 81 insertions(+), 181 deletions(-)

Fusion-Task-Id: FN-6982

Fusion-Task-Lineage: b880df6c-d84b-4d34-886b-f54c1d568ece
2026-06-24 23:32:52 -07:00
gsxdsm
f918896839 FN-6981: make Git Manager tabs scroll on mobile
Keep Git Manager navigation reachable in narrow mobile and embedded layouts.

- Make Git Manager tab strips non-wrapping horizontal scrollers with touch-friendly panning.
- Constrain repo selector sizing so tabs and Refresh stay reachable on narrow widths.
- Extend modal and CSS coverage for standalone, embedded, and workspace-selector mobile cases.
- Add a patch changeset for the published CLI package.

Files changed:
 .changeset/fn-6981-git-manager-mobile-tabs.md      |  7 +++
 packages/dashboard/app/components/ScriptsModal.css | 43 ++++++++++++-
 .../components/__tests__/GitManagerModal.test.tsx  | 59 +++++++++++++-----
 .../__tests__/core-modals-mobile.test.tsx          | 70 +++++++++++++++++++++-
 4 files changed, 160 insertions(+), 19 deletions(-)

Fusion-Task-Id: FN-6981

Fusion-Task-Lineage: c8a2910c-3642-4e14-a841-21cc32bc4cdb
2026-06-24 23:32:52 -07:00
gsxdsm
193532bd2b FN-6968: refresh test timing snapshots
Refresh the weekly test velocity baseline with current timing evidence.

- Update the rendered baseline report with the latest capture timestamps and velocity deltas.
- Replace stale slowest-file timing rows with the refreshed snapshot entries.
- Append the latest velocity history sample used by the weekly report.

Files changed:
 docs/test-velocity-baseline.md     |   61 +-
 scripts/test-timings.json          | 1772 +++++++++++++++++++++++++++++-------
 scripts/test-velocity-history.json |  112 +++
 3 files changed, 1580 insertions(+), 365 deletions(-)

Fusion-Task-Id: FN-6968

Fusion-Task-Lineage: 940f3581-3650-4feb-8730-d6dbb760e30c
2026-06-24 23:32:52 -07:00
gsxdsm
5a192ece16 FN-6946: add GitHub reference picker to task dialog
Add a GitHub issue and pull request picker that seeds new task prompts from detected remotes.

- Add remote-aware GitHub issue/PR selection to the New Task modal.
- Generate focused task descriptions for selected issues and pull requests while confirming before replacing user text.
- Style and document the compact picker and cover loading, errors, remote selection, and overwrite behavior in tests.
- Add a changeset for the published Fusion CLI package.

Files changed:
 .changeset/fn-6946-github-reference-picker.md      |   7 +
 docs/dashboard-guide.md                            |   2 +
 packages/dashboard/app/components/NewTaskModal.css |  53 ++++
 packages/dashboard/app/components/NewTaskModal.tsx | 297 ++++++++++++++++++++-
 .../app/components/__tests__/NewTaskModal.test.tsx | 199 +++++++++++++-
 5 files changed, 555 insertions(+), 3 deletions(-)

Fusion-Task-Id: FN-6946

Fusion-Task-Lineage: e871827c-dfe5-4fc3-a525-c1161639d306
2026-06-24 23:32:52 -07:00
gsxdsm
3ae053e744 FN-6976: keep planning JSON failures retryable
Persist malformed Planning Mode AI responses as retryable errors while improving JSON response selection.

- Preserve failed initial-turn planning sessions with actionable retry errors instead of deleting them.
- Prefer valid planning-shaped JSON candidates over unrelated embedded JSON blobs.
- Cover streaming, non-streaming, retry recovery, and AI merge test mock compatibility.
- Add a patch changeset for the published CLI package.

Files changed:
 .changeset/fn-6976-planning-json-recovery.md       |   7 +
 .../cli/src/commands/__tests__/dashboard.test.ts   |   1 +
 packages/cli/src/commands/__tests__/task.test.ts   |   9 +-
 .../src/__tests__/session-error-recovery.test.ts   | 144 +++++++++++++++++++++
 packages/dashboard/src/planning.ts                 | 123 ++++++++++--------
 5 files changed, 229 insertions(+), 55 deletions(-)

Fusion-Task-Id: FN-6976

Fusion-Task-Lineage: e1cb59cb-1d7d-4aff-b17f-9633568f823f
2026-06-24 23:32:52 -07:00
gsxdsm
eb3833a542 FN-6971: retire dual-observe cutover gating
Remove stale dual-observe prerequisites from workflow-authoritative readiness while preserving parity-summary safeguards.

- Require the authoritative flag plus clean populated parity summaries for interpreter cutover readiness.
- Keep persisted workflowInterpreterDualObserve values inert in runtime tests and documentation.
- Update cutover, parity, and graph-executor tests to reflect retired shadow observation behavior.
- Add a formatted patch changeset for the operator-facing cutover readiness fix.

Files changed:
 .changeset/fn-6971-workflow-cutover-readiness.md   |  7 ++
 docs/architecture.md                               |  2 +-
 docs/settings-reference.md                         |  4 +-
 docs/workflow-steps.md                             | 19 +++--
 .../core/src/__tests__/workflow-cutover.test.ts    | 19 +++--
 packages/core/src/workflow-cutover.ts              |  9 +--
 .../workflow-interpreter-cutover.test.ts           | 87 +++++++++++++++++++---
 .../workflow-interpreter-dual-observe.test.ts      | 76 ++++++-------------
 .../src/__tests__/stepwise-workflow-parity.test.ts | 25 ++++---
 .../engine/src/workflow-authoritative-driver.ts    | 10 +--
 10 files changed, 152 insertions(+), 106 deletions(-)

Fusion-Task-Id: FN-6971
Fusion-Task-Lineage: 363a441d-62e5-403c-9389-75fcf788352a
2026-06-24 23:32:52 -07:00
gsxdsm
e7021857a9 FN-6999: equalize mobile nav side spacing
Mobile bottom navigation now keeps balanced horizontal spacing without disturbing existing viewport compensation.\n\n- Add tokenized inline padding to the fixed mobile nav bar while preserving ICB and safe-area behavior.\n- Cover symmetric side spacing in mobile nav CSS and component layout tests.\n- Add a patch changeset for the published CLI package.\n\nFiles changed:\n .changeset/fn-6999-mobile-nav-side-spacing.md                  |  7 +++++++\n packages/dashboard/app/__tests__/mobile-nav-bar-css.test.ts    | 10 ++++++++++\n packages/dashboard/app/components/MobileNavBar.css             |  5 +++++\n .../dashboard/app/components/__tests__/MobileNavBar.test.tsx   |  8 ++++++++\n 4 files changed, 30 insertions(+)

Fusion-Task-Id: FN-6999

Fusion-Task-Lineage: 91bd2174-8e5d-4227-a035-ff621e309719
2026-06-24 23:32:52 -07:00
gsxdsm
e473ba68fb FN-6997: widen task changes diff panel
Reclaims task detail padding so inline diffs have more readable width on compact screens.

- Expand the compact Changes file list to consume detail-body padding without page overflow.
- Add mobile breakpoint coverage that matches the detail-body padding contract.
- Add regression tests for the phone-width inline diff surface and CSS rules.
- Add a patch changeset for the published Fusion CLI package.

Files changed:
 .changeset/fn-6997-diff-panel-width.md             |  7 ++
 .../dashboard/app/components/TaskChangesTab.css    | 20 ++++-
 .../components/__tests__/TaskChangesTab.test.tsx   | 98 +++++++++++++++++++++-
 3 files changed, 121 insertions(+), 4 deletions(-)

Fusion-Task-Id: FN-6997

Fusion-Task-Lineage: a7f21c0d-89a8-430e-a84c-341a10af4a4a
2026-06-24 23:32:52 -07:00
gsxdsm
29530b5fdb FN-6967: widen tablet agent chat bubbles
Improve tablet Chat readability by widening agent-side response bubbles without expanding user or Quick Chat bubbles.

- Add a ChatView container query that widens assistant, streaming, and failure bubbles on tablet-width containers.
- Cover the targeted bubble classes and CSS width contract in ChatView tests.
- Document the tablet behavior and add a structured patch changeset for the published CLI bundle.

Files changed:
 .changeset/fn-6967-tablet-chat-bubble-width.md     |  7 +++
 docs/dashboard-guide.md                            |  3 +-
 packages/dashboard/app/components/ChatView.css     | 12 +++++
 .../app/components/__tests__/ChatView.test.tsx     | 56 ++++++++++++++++++++++
 4 files changed, 77 insertions(+), 1 deletion(-)

Fusion-Task-Id: FN-6967
Fusion-Task-Lineage: 74195885-4ec0-4fc9-b628-0b0510129026
2026-06-24 23:32:52 -07:00
gsxdsm
a554ceb672 FN-6959: align footer launcher typography
Align the Quick Chat and Terminal footer launchers so they read as matching peer controls.

- Inherit the footer font and color contract for the Terminal footer launcher.
- Preserve usable hover, focus, padding, and scripts chevron behavior in the footer variant.
- Document the footer Quick Chat launcher placement and add release notes.
- Expand status bar tests to lock the shared footer launcher styling.

Files changed:
 .changeset/fn-6959-footer-launcher-style.md        |   7 ++
 docs/dashboard-guide.md                            |   2 +-
 .../dashboard/app/components/ExecutorStatusBar.css |   2 +-
 .../dashboard/app/components/TerminalLauncher.css  |  37 +++++++-
 .../__tests__/ExecutorStatusBar.test.tsx           | 105 +++++++++++++++++++--
 5 files changed, 138 insertions(+), 15 deletions(-)

Fusion-Task-Id: FN-6959
Fusion-Task-Lineage: 539307cb-dc81-441f-8baf-8823826e2195
2026-06-24 23:32:52 -07:00
gsxdsm
f955606106 FN-6966: flag stale timing metadata in velocity reports
Add stale timing context to the weekly test velocity baseline so W26 regressions are not misattributed.

- Report timing snapshot age and missing slowest-file paths before the slow-file table.
- Persist timing metadata notes in velocity history and regenerate the W26 baseline report.
- Cover stale and missing timing metadata warnings with node:test assertions.

Files changed:
 docs/test-velocity-baseline.md                    |  23 ++-
 scripts/__tests__/test-velocity-baseline.test.mjs |  26 +++
 scripts/test-velocity-baseline.mjs                |  65 +++++-
 scripts/test-velocity-history.json                | 231 ++++++++++++++++++++++
 4 files changed, 330 insertions(+), 15 deletions(-)

Fusion-Task-Id: FN-6966

Fusion-Task-Lineage: bccac6c2-11c1-4295-b3af-b77dab1748dc
2026-06-24 23:32:52 -07:00
gsxdsm
8c734fe230 FN-6973: stabilize engine liveness tests
Stabilize engine tests around executor liveness cleanup and scheduler handoff expectations.

- Assert task-move cleanup clears active session registry entries for agent, step, and workflow-step sessions.
- Reset active session registry module state between executor tests alongside executing task locks.
- Relax brittle worktree and scheduler assertions to match current merge-base and handoff behavior.

Files changed:
 .../engine/src/__tests__/executor-pause.test.ts    | 52 +++++++++++++++++++---
 .../engine/src/__tests__/executor-test-helpers.ts  |  8 ++--
 .../engine/src/__tests__/executor-worktree.test.ts |  2 +-
 .../owning-node-unavailable-interactions.test.ts   |  2 +-
 .../src/__tests__/restart.integration.test.ts      |  8 +++-
 5 files changed, 59 insertions(+), 13 deletions(-)

Fusion-Task-Id: FN-6973

Fusion-Task-Lineage: 37c7d4b3-7ff5-4339-bd3f-6b10f507883e
2026-06-24 23:32:52 -07:00
gsxdsm
214a60c085 FN-6944: reclaim quick entry textarea width
Quick entry textareas now avoid inheriting refine-button padding that caused right-side dead space.

- Override quick-entry textarea right padding with a selector specific enough to beat the shared description refine rule without !important.
- Add CSS cascade regression coverage confirming quick entry reclaims width while global description textareas retain overlay padding.
- Add a patch changeset for the published CLI package.

Files changed:
 .changeset/fn-6944-quick-entry-width.md            |   7 ++
 .../dashboard/app/components/QuickEntryBox.css     |   6 +-
 .../components/__tests__/QuickEntryBox.test.tsx    | 114 +++++++++++++++++++++
 3 files changed, 124 insertions(+), 3 deletions(-)

Fusion-Task-Id: FN-6944

Fusion-Task-Lineage: 2a6c7190-ff87-4da6-9437-8e57a113ce56
2026-06-24 23:32:51 -07:00
gsxdsm
d3593065e4 FN-6960: bound PR metadata generation
Keep Create PR metadata generation responsive and ensure fallback PR content remains editable.

- Race metadata collection, prompt execution, and session creation against abort/timeout signals.
- Return fallback PR metadata from the API route when generation exceeds the route budget.
- Seed the Create PR dialog with editable fallback body content after metadata failures and require a non-empty body before submit.
- Validate non-empty PR bodies in both gh CLI and API-backed PR creation paths.
- Add regression coverage for bounded metadata generation, fallback responses, and PR body validation.

Files changed:
 .changeset/fn-6960-pr-metadata-bounded.md          |  7 ++
 .../dashboard/app/components/PrCreateModal.tsx     | 34 ++++++++-
 .../components/__tests__/PrCreateModal.test.tsx    | 41 +++++++++++
 .../src/__tests__/github-create-pr.test.ts         |  6 +-
 .../src/__tests__/github-forced-mode.test.ts       |  2 +-
 packages/dashboard/src/__tests__/github.test.ts    | 21 ++----
 .../src/__tests__/pr-metadata-generator.test.ts    | 55 ++++++++++----
 .../src/__tests__/pr-routes.contract.test.ts       | 10 +++
 .../register-git-github.pr-errors.test.ts          |  2 +-
 ...it-github.pr-options-preflight-metadata.test.ts | 25 +++++++
 .../dashboard/src/__tests__/routes-auth.test.ts    | 12 +--
 .../dashboard/src/__tests__/routes-github.test.ts  |  2 +-
 packages/dashboard/src/github.ts                   | 20 ++++-
 packages/dashboard/src/pr-metadata-generator.ts    | 86 ++++++++++++++--------
 .../dashboard/src/routes/register-git-github.ts    | 67 +++++++++++++----
 15 files changed, 301 insertions(+), 89 deletions(-)

Fusion-Task-Id: FN-6960
Fusion-Task-Lineage: 1ae72062-7180-4de3-999b-98d131f00792
2026-06-24 23:32:51 -07:00
gsxdsm
d7f3c7093e FN-6941: add graph workflow filtering
Adds a Graph view header workflow dropdown that filters dependency graph tasks by the selected workflow.

- Portal the workflow switcher into the Graph view header and clear selection when unmounted.
- Scope plugin graph task context using workflow assignments and default workflow fallback.
- Cover the header integration, task filtering, portal lifecycle, and disabled workflow mode.
- Document Graph workflow filtering and add the published package changeset.

Files changed:
 .changeset/fn-6941-graph-workflow-dropdown.md      |   7 +
 docs/dashboard-guide.md                            |   1 +
 packages/dashboard/app/App.tsx                     |   4 +
 .../app/__tests__/graph-workflow-header.test.tsx   | 127 +++++++++++++++
 .../app/components/GraphWorkflowSwitcherSlot.tsx   | 110 +++++++++++++
 .../__tests__/GraphWorkflowSwitcherSlot.test.tsx   | 173 +++++++++++++++++++++
 .../app/components/dashboard/MainContent.tsx       |  23 ++-
 .../dashboard/app/components/dashboard/types.ts    |   3 +
 8 files changed, 446 insertions(+), 2 deletions(-)

Fusion-Task-Id: FN-6941

Fusion-Task-Lineage: a77bc99e-00fe-42fe-a976-d88ffd803388
2026-06-24 23:32:51 -07:00
gsxdsm
533c8af4bf docs: refresh README media with live captures across all themes incl. Ember (#1737)
## Summary

Re-captured all README showcase GIFs and screenshots from the running
Fusion dashboard, refreshed across every theme including the new Ember
variant.

## What changed

**Media (32 assets: 24 refreshed, 8 new):**
- Dashboard board + Agents roster shots (Tokyo Night + Ember)
- Command Center, Workflows, Agent Chat, Chat Rooms, Agent Mail GIFs
refreshed across Tokyo Night, Shadcn Light, Shadcn Dark Gray, and Ember
- 6 mobile screenshots (board, agents, missions, command center, chat,
chat list)
- All captures use a 1600x1000 viewport (16:10) at deviceScaleFactor 1
to match the original committed media and avoid vertical squashing from
vh-based CSS

**README.md:**
- Added a "board & agent team" showcase section with live dashboard and
agents shots
- Expanded every theme table from 2 columns (Light / Dark Gray) to 3
(Light / Dark Gray / Ember)

**demo/seed.ts:**
- Extended to seed chat messages and agent mail (5 agents, a direct chat
session, a #leads multi-agent room, and 6 mailbox messages covering
triage summaries, approvals, and hand-offs)
- Fixed two pre-existing task-transition bugs so the full seed runs to
completion

## Capture method

Desktop and agent shots were captured read-only from the live Fusion
instance (port 4040). Agent mail was captured from a throwaway seeded
instance — no running Fusion data was modified.


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

---

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

* **Documentation**
* Updated feature showcase galleries with expanded theme coverage,
including new Ember theme variants alongside existing Shadcn themes
across all sections.

* **New Features**
* Demo environment enhanced with agent collaboration capabilities
including multi-agent chat rooms, direct messaging, and mailbox
features.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2026-06-24 23:20:48 -07:00
gsxdsm
c34a59edf7 Merge branch 'main' into feature/readme-mobile 2026-06-24 23:20:39 -07:00
gsxdsm
efa97c82e3 refactor(dashboard): break App.tsx into smaller modules (#1740)
## Summary

Behavior-preserving decomposition of the dashboard root
`packages/dashboard/app/App.tsx` — a 2,729-line monolith (~2,350-line
single `AppInner` component) — into focused hooks, a pure-helpers util,
and presentational components. **No functional change.**

**`App.tsx`: 2,729 → 1,636 lines**, now under the 2,000-line file-count
ratchet and graduated off its grandfathered baseline (can never regress
above 2,000).

## What was extracted
- **Pure helpers + constants** → `utils/appLifecycle.ts` (re-exported
from `App` so the 7 unit-tested symbols keep their `from "../../App"`
import contract).
- **12 custom hooks** under `app/hooks/`: `useMailboxUnread`,
`useChatUnreadBadge`, `useStashOrphanCount`, `useApprovalBanner`,
`useBranchTaskFilters`, `useDashboardHealth`, `useAuthTokenRecovery`,
`useScopedDismissFlag`, `useCapacityRiskBanner`,
`useMainPanelTaskDetail`, `useBoardScrollRestore`, `usePoppedOutTasks`.
- **2 presentational components** under `app/components/dashboard/`:
`MainContent` (the 647-line view-switch dispatcher) and
`DashboardBanners`, with typed prop interfaces in `types.ts`.

## Key design decisions
- **KTD4 — single `task:updated` subscriber preserved.** The original
one `/api/events` subscriber (mailbox counts + approval banner +
GitHub-star + awaiting-approval refresh) was split: `useApprovalBanner`
owns `task:updated`/`approval:requested` (banner + star +
`onMailboxRefresh` callback); `useMailboxUnread` owns
`message:*`/`approval:*` count refresh. `subscribeSse` multiplexes onto
one shared `EventSource`, so no extra connections.
- **The 18 lazy view declarations stay in `App.tsx`** (the
`lazy-loaded-views-docs.test.ts` guard regex-scans `App.tsx`) and are
threaded to `MainContent` as `LazyExoticComponent` props.
- **Eager `ChatView.css` import stays at the `App.tsx` top level** (FOUC
prevention).

## Verification
- `pnpm lint` (full repo), `typecheck` (both passes), `pnpm build` — all
clean.
- `App.test.tsx` (the full-render behavior contract) **identical**
pre/post: 123 pass / 5 fail, where the 5 are **pre-existing**
experimental-flag failures verified against unmodified `App.tsx`.
- `lazy-loaded-views-docs.test.ts` passes.
- 30+ new `renderHook` tests, incl. a `sseSplitIntegration` test
co-mounting both SSE hooks to pin the split (no double-fire;
awaiting-approval refresh exactly once).
- `App.tsx` graduated off the line-count ratchet baseline (scoped — only
its entry removed).

## Notes
- `useShellOnboarding` was deferred — the shell region is more
intertwined than planned (onboarding + connection-status + local/remote
redirects).
- The browser/visual smoke wasn't run (requires the server stack);
coverage rests on the green gate + fresh build + `App.test.tsx`
rendering the real `<App/>`.

Plan:
`docs/plans/2026-06-24-001-refactor-dashboard-app-tsx-module-breakup-plan.md`
(status: completed).

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

---

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

* **Refactor**
* Split dashboard main content and banner rendering into dedicated
components and presenter-style logic.
* Moved unread counters, approval/notification banners, dashboard health
refresh, scroll restoration, task detail flow, branch filtering, and
per-project dismissals into dedicated hooks.
* Centralized dashboard lifecycle helpers to keep banner/session
behavior consistent.
* **Documentation**
* Added a plan describing the behavior-preserving dashboard module
breakup.
* **Tests**
* Added/expanded hook and integration coverage for unread badges,
approval banners, scroll restore, branch filtering, dismissals, health
refresh, and main-panel task detail behavior.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2026-06-24 20:43:41 -07:00
gsxdsm
fd63260962 Merge branch 'main' into feature/refactor-apptsx 2026-06-24 20:31:59 -07:00
gsxdsm
2ba81c25b4 chore(release): v0.47.0
Version bump via changesets.
2026-06-24 19:32:59 -07:00
gsxdsm
3630da0658 feat: Better changelog — structured changesets + distilled release notes (#1750)
## Summary

Replaces dense, agent-authored technical changeset paragraphs with a
**structured, concise changeset schema** (end-user summary + category +
optional dev detail), enforced by a linter. Adds a **deterministic
distillation step** at version time that transforms a release's
collected changesets into clean, grouped, end-user-facing release notes.
Unifies both release paths (local `release.mjs` and CI `version.yml` /
`release.yml`) behind a single distilled artifact so the root
`CHANGELOG.md` and GitHub Release both carry the same user-facing notes.

## Changes

### Changeset format (`scripts/lib/changeset-schema.mjs`)
- Each changeset body now uses labeled fields: `summary` (required,
user-facing, max 120 chars), `category` (required:
feature/fix/breaking/security/performance/internal), `dev` (optional
developer detail)
- Legacy freeform changesets are detected and flagged for the transition
period

### Linter (`scripts/check-changeset-format.mjs`)
- Validates structured schema, summary length, category enum, and
frontmatter package scope
- Wired into `test:gate`, `pretest`, `pretest:full`, and `pr-checks.yml`
- Legacy changesets warn (exit 0) during transition; `--strict` flag
fails on them

### Distillation (`scripts/lib/distill-release-notes.mjs`)
- `distillDeterministic` builds grouped, end-user-facing release notes
by category (New, Fixed, Breaking, Security, Performance, Internal)
- `buildDistillationPrompt` and `DISTILLATION_SYSTEM_PROMPT` ready for
AI distillation via `createFnAgent` when model credentials are available
- Graceful fallback: deterministic bullet list when no model is
configured

### Release integration
- **Local path** (`scripts/release.mjs`): captures changeset entries
before `changeset version` deletes them, distills notes post-version,
replaces the version's CHANGELOG section with curated notes
- **CI path** (`scripts/ci-distill-release-notes.mjs`,
`scripts/run-ci-distill.mjs`): chained into `release:version` so both
flows get distilled notes
- **GitHub Release** (`release.yml`): uses curated CHANGELOG notes
instead of `generate_release_notes: true`

### Documentation
- `AGENTS.md`, `RELEASING.md`, `docs/contributing.md` updated with the
structured format guide
- `.changeset/README.md` template for `pnpm changeset` consumers

## Testing

- 54 new unit tests across changeset-schema, check-changeset-format,
distill-release-notes, and extract-version-notes
- `pnpm lint` clean
- `pnpm test:gate` green (371 tests)
- `pnpm check:changesets` passes (13 legacy warnings expected during
transition)

## Plan

Full implementation plan:
`docs/plans/2026-06-24-001-feat-better-changelog-plan.md`

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

---

<a href="https://stagereview.app/Runfusion/Fusion/pull/1750">
  <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 structured release-note guidance and validation for changeset
entries.
* Release notes now use curated, grouped changelog content instead of
auto-generated text.
* Added a fallback process to keep release notes consistent when
automated distillation isn’t available.

* **Bug Fixes**
  * Improved handling of legacy changesets and malformed entries.
* Ensured version-specific changelog sections are updated without
affecting older releases.

* **Tests**
* Added coverage for changeset validation, note grouping, and changelog
section replacement.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2026-06-24 19:31:18 -07:00
gsxdsm
2019e5a30f feat(changelog): U6 — docs, agent guidance, and structured changeset
Update AGENTS.md, RELEASING.md, contributing.md with the structured
changeset format guide. Add .changeset/README.md template. Add changeset
for this change. Update distill-release-notes.mjs with final version.
2026-06-24 19:25:51 -07:00
gsxdsm
51d1cc521c feat(changelog): U5 — CI workflow distillation integration
Chain distillation into release:version so both local and CI versioning
flows get distilled notes. Update release.yml to use curated CHANGELOG
notes instead of GitHub's auto-generated release notes.
2026-06-24 19:25:44 -07:00
gsxdsm
901f7124f3 feat(changelog): U4 — wire distillation into local release path
Capture changeset entries before 'changeset version' deletes them,
distill deterministic notes post-version, and replace the version's
section in the root CHANGELOG with curated end-user notes.
2026-06-24 19:25:38 -07:00
gsxdsm
8dfca7ab98 chore: downgrade optional-steps changeset from major to minor
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-24 19:23:55 -07:00
gsxdsm
c73dcad911 fix(workspace): suppress spurious 'Not a git repository' toast in Git Manager (#1749)
## Problem
Opening the Git Manager on a multi-repo **workspace** project rendered
the sub-repo dropdown correctly but **also toasted "Not a git
repository"** every time.

## Cause
On open, the section data fetch fires immediately with no `repoPath`
(the `selectedRepo` hasn't resolved yet), so the git status request
targets the **non-git browse-only workspace root** and fails `Not a git
repository`. `fetchWorkspaceRepos` resolves a tick later and the fetch
re-runs against a real sub-repo — but the first failure had already
toasted.

## Fix
Track workspace detection in a ref and suppress that single benign
root-race error: no `repoPath` + `Not a git repository` while detection
is still pending or has confirmed a workspace. A genuinely broken
**non-workspace** project still surfaces the error — once detection
settles as non-workspace, a single *guarded* re-fetch re-surfaces it.
The guard ensures no redundant fetch in the common non-workspace-OK
path, preserving existing call-count expectations.

## Tests
- Added the missing `fetchWorkspaceRepos` api mock — a pre-existing gap
(from the git-manager commit) that had broken the **entire**
GitManagerModal suite at import (130 tests).
- Positive regression: workspace project → no `Not a git repository`
toast.
- Negative control: real non-workspace broken repo → the error still
toasts.

Dashboard typecheck + lint clean; my 2 new tests pass and no
previously-passing test regressed (8 unrelated pre-existing failures
remain, untouched by this change).

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

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

---

<a href="https://stagereview.app/Runfusion/Fusion/pull/1749">
  <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 tasks now return aggregated, repo-prefixed diff results for
both detailed diffs and file-diff listings, including consistent
file/stats aggregation across sub-repositories.
* **Bug Fixes**
* Prevented an initial, harmless “Not a git repository” message from
being shown during early workspace detection; genuine errors still
display the standard error toast.
* Improved workspace detection and retry behavior so the UI loads the
correct repo context after project switching.
* Updated the Task Changes UI to handle workspace tasks without
incorrectly showing the “No worktree available” state.
* **Tests**
* Added/expanded coverage for workspace diff aggregation and
workspace-related UI/error behaviors.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2026-06-24 19:18:12 -07:00
gsxdsm
9793ffbf9b Merge branch 'main' into feat/dashboard-workspace-wizard 2026-06-24 19:09:07 -07:00
gsxdsm
e6e096645a fix(workspace): address code-review findings on the workspace diff + Git Manager
From the multi-agent /ce-code-review of PR #1749 (no P0/P1 correctness bugs; these
are perf, race-hardening, and convention fixes):

- P1 (perf/reliability): the workspace diff ran git subprocesses serially per
  sub-repo AND per file — an N×M explosion with no aggregate cap. Add a bounded
  mapWithConcurrency helper (order-preserving) and parallelize the per-file patch
  loop (cap 8) and the per-sub-repo loop (cap 4). Deleted files still fetch their
  patch (skipping it would drop deletes from /file-diffs and zero /diff stats).
- P2 (frontend race): GitManagerModal's workspace-detection could be clobbered by
  a previous project's in-flight fetch on a rapid projectId switch / close-reopen.
  Add a detectionGenerationRef guard — only the latest detection run may mutate
  state; the effect cleanup bumps the generation to abandon superseded runs.
- P2 (DRY): reuse the existing parseStatusCode instead of re-inlining the
  status-code mapping.
- P2 (convention): FNXC-tag the new functions/branches per CLAUDE.md.
- P3: extract DIFF_TIMEOUT_MS/FILE_DIFFS_TIMEOUT_MS constants, drop a dead
  catch-assignment, note the done-fallback oldPath limitation.

Tests: order-preservation after parallelization; rapid-project-switch generation
guard (a stale workspace verdict must not suppress a new project's real error).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-24 19:05:11 -07:00
gsxdsm
98fc453f45 feat(workspace): show per-sub-repo changes in the task Changes tab
The Changes / Files-changed tab showed nothing for multi-repo workspace tasks:
the task-diff backend is single-repo throughout, and a workspace task has null
task.worktree/task.branch (its per-repo state lives in workspaceWorktrees), so
every path fell back to git-diff against the non-git workspace root → empty.

Backend (register-session-diff-routes): extract the single-repo per-worktree
detailed-diff into one shared helper (computeWorktreeDetailedFiles) and add a
workspace branch to BOTH /tasks/:id/diff and /tasks/:id/file-diffs that runs
before the single-repo logic: iterate sorted workspaceWorktrees, compute each
sub-repo's diff in its own live worktree against that repo's baseCommitSha (done
tasks fall back to the per-repo landed range in the sub-repo root), and aggregate
with `${repoRel}/`-prefixed paths. Single-repo behavior is byte-for-byte
preserved (renamed→modified fold retained; 58 existing diff-route tests pass).

Frontend: TaskChangesTab takes an isWorkspace prop and no longer shows the
single-repo "No worktree available" empty state for workspace tasks;
TaskDetailModal passes isWorkspace={isWorkspaceTask(workingTask)}.

Tests: backend aggregation (repo-prefixed paths + stats) and frontend rendering
of workspace changes instead of the empty state.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-24 18:29:58 -07:00
gsxdsm
e4a0118579 fix(workspace): suppress spurious "Not a git repository" toast in Git Manager
Opening the Git Manager on a workspace project rendered the sub-repo dropdown
correctly but ALSO toasted "Not a git repository". On open the section fetch
fires immediately with no repoPath (selectedRepo unresolved), hitting the non-git
browse-only workspace root; fetchWorkspaceRepos resolves a tick later and the
fetch re-runs against a real sub-repo. We now track workspace detection in a ref
and suppress that one benign root-race error (no repoPath + "Not a git
repository" while detection is pending or has confirmed a workspace). A genuinely
broken non-workspace project still surfaces the error: once detection settles as
non-workspace, a single guarded re-fetch re-surfaces it (no redundant fetch in
the common non-workspace path, preserving existing call-count expectations).

Tests: add the missing fetchWorkspaceRepos api mock (pre-existing gap that broke
the whole GitManagerModal suite at import), plus a positive (workspace → no
toast) and negative-control (non-workspace broken → toast) regression.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-24 18:09:39 -07:00
gsxdsm
de3e84a0cd fix(workspace): remove false reattach banner, silence ai-merge ENOENT prune, skip workspace in rebinder (#1748)
Follow-up polish on the multiworkspace merge flow (after #1747 merged).
Driven by live testing of multi-repo tasks.

## Changes

**1. Remove the "Branch needs reattachment" banner (dashboard)**
The banner fired for any in-review task with a null singular
`task.branch` — which is the **normal, healthy** state for a workspace
task (its attachment is the per-sub-repo worktrees in
`workspaceWorktrees`, not a root branch). So it was a permanent false
positive on every in-review workspace task. Genuine lost bindings
(non-workspace) are already reattached **automatically** by
self-healing's `reconcileInReviewBranchRebind`, which runs event-driven
on move-to-in-review and on the sweep — no manual user action needed.
Removed the banner UI, its handler/state/imports, the obsolete
`rebind-banner` test, and its registry entry.

**2. self-healing: skip workspace tasks in the branch rebinder
(engine)**
`reconcileInReviewBranchRebind` now explicitly skips workspace tasks.
They are never rebind candidates — their `fusion/<id>` branches live
inside each sub-repo, not in the non-git browse-only workspace root, so
a null root branch is healthy. (The slim list select now carries
`workspaceWorktrees` from #1747, so `isWorkspaceTask` is accurate on
slim rows.)

**3. merger-ai: silence ENOENT in the pre-merge prune (engine)**
The AI-merge pre-merge prune `readdirSync`'d `<repo>/.fusion/ai-merge`
and warned on every workspace merge because that clean-room root is
created lazily and is normally absent. ENOENT now means "nothing to
prune" — skipped silently; non-ENOENT failures (and the tmpdir case)
still surface/throw.

**4. test: add `ToggleRight` to the TaskDetailModal lucide mock**
Pre-existing gap from FN-6880 (optional-group node) that broke the
entire TaskDetailModal suite at import; unrelated to this change but
blocking validation.

## Validation
- engine typecheck + build clean; dashboard typecheck clean
- engine rebind + merger-ai suites pass (63); TaskDetailModal suites
pass (117) after the mock fix
- lint clean on changed files

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

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

---

<a href="https://stagereview.app/Runfusion/Fusion/pull/1748">
  <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**
* Removed the “Branch needs reattachment” banner and reattach action
from task details.
* Workspace tasks are skipped during branch rebind checks to avoid
confusing recovery prompts.
  * Temporary AI-merge cleanup now handles missing directories quietly.
* Workspace per-repo landing now continues when dependency
synchronization fails (with degraded dependency availability), while
single-repo landing still fails fast.
* **Tests**
* Updated component test mocks and removed the rebind-banner test
coverage.
* Added coverage for resilient workspace dependency synchronization
behavior and adjusted workspace-merger test fixtures.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2026-06-24 17:45:01 -07:00
gsxdsm
9dc228549c fix(workspace): make per-repo land resilient to a sub-repo dependency-sync failure
A workspace land squash-merges each sub-repo via a clean room that first runs the
configured/inferred install (npm/pnpm/yarn). The install hard-fails by design so
merge verification never runs against an uninstalled checkout — but that let ONE
sub-repo with a manifest npm refuses to install (e.g. a corrupt `-@0.0.1`
lockfile entry rejected by npm 11) block landing every other sub-repo.

landWorkspaceTask now passes nonFatalDependencySync to landOneRepo: a clean-room
install failure is caught, logged + audited as a non-fatal degradation, and the
land proceeds (the git squash needs no installed deps; only dep-dependent
verification degrades for that repo). A real abort signal still propagates. The
single-repo land path keeps the documented hard-fail (flag defaults off).

Tests: new workspace-merger-deps-resilient asserts both the resilient workspace
land (all repos land despite install throwing) and the preserved single-repo
hard-fail. Also fix a pre-existing getTask mock gap in workspace-merger.test
(mergeAndReview reads getTask().comments) that broke 3 tests at the land step.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-24 17:15:38 -07:00
gsxdsm
7793e185bc Merge branch 'main' into feat/dashboard-workspace-wizard 2026-06-24 17:12:05 -07:00
gsxdsm
ea67e4424d Merge branch 'main' into feature/refactor-apptsx 2026-06-24 17:07:22 -07:00
gsxdsm
93cfb88e64 fix(workspace): remove false reattach banner, silence ai-merge ENOENT prune
- dashboard: remove the "Branch needs reattachment" banner. It fired for any
  in-review task with a null singular task.branch — the NORMAL state for a
  workspace task (attachment is per-sub-repo worktrees in workspaceWorktrees), so
  it was a permanent false positive. Genuine lost bindings are already reattached
  automatically by self-healing's reconcileInReviewBranchRebind (event-driven on
  move-to-in-review + sweep), so no manual user action is needed. Delete the
  now-obsolete rebind-banner test + its registry entry.
- engine/self-healing: reconcileInReviewBranchRebind now explicitly skips
  workspace tasks (never rebind candidates — their fusion/<id> branches live in
  the sub-repos, not the non-git browse root; null root branch is healthy).
- engine/merger-ai: pre-merge prune treats an absent ai-merge search root (ENOENT)
  as "nothing to prune" instead of warning on every workspace merge.
- test: add ToggleRight to the TaskDetailModal lucide mock (pre-existing gap from
  FN-6880 that broke the whole suite at import).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-24 16:25:23 -07:00
gsxdsm
51b48d7fd1 feat(workspace): multi-repo git-manager + fix multiworkspace task completion (#1747)
## Summary

This branch carries the multi-repo workspace wizard/git-manager work
plus a fix for two bugs that prevented multiworkspace tasks from
completing at all.

### Bug fixes (this session)

**1. `fn_task_done` always failed with "acquired no sub-repo
worktrees"**
`task.workspaceWorktrees` had **no SQLite column, no `rowToTask`
mapping, and no migration** — unlike every sibling JSON field (e.g.
`mergeDetails`). `fn_acquire_repo_worktree` → `updateTask({
workspaceWorktrees })` set it in memory only; `applyTaskPatch` then
wrote the DB-round-tripped task (which dropped the field) back to
`task.json`, so it was silently lost on every persist. Every later
`getTask` returned `undefined`, so the scope verifier read `{}` →
`repoKeys.length === 0` → blocked. Confirmed against the live failing
tasks: both `MULT-001`/`MULT-002` `task.json` had zero
`workspaceWorktrees` despite logs showing the acquired `swarmclaw`
worktree. **Fix:** persist it mirroring `mergeDetails` (SCHEMA_SQL
column + v129 migration + `db-migrate` + `defineTaskColumn` + `TaskRow`
+ `rowToTask`).

**2. Concurrent workspace tasks collided on the shared browse-root**
In workspace mode every task runs rooted at the shared non-git workspace
root, and `setActiveSession` registered that path keyed only by path —
so the foreign-task guard rejected the second concurrent task
("active-session path … is held by …"), letting only one task per
workspace run. **Fix:** `sessionRegistryPath()` gives each workspace
task a task-scoped synthetic session key, applied symmetrically at all 3
register + 3 unregister sites. The in-memory worktree Set still holds
the real root; per-sub-repo exclusivity (the workspace-repo-acquire
lease) is unchanged.

### Tests
- Core: `workspaceWorktrees` round-trips across `getTask`, `listTasks`,
and a full store reopen; empty map normalizes to `undefined`.
- Engine: two concurrent workspace tasks register executor/step/workflow
sessions without collision; cleanup leaves no leak; non-workspace
clobber guard preserved.
- Both suites verified to **fail without the fix for the right reason**.
Gate suite, typecheck, lint, and build all green.

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

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

---

<a href="https://stagereview.app/Runfusion/Fusion/pull/1747">
  <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-aware Git repo selection so Git actions can target a
specific sub-repository.
* Extended dashboard Git/GitHub APIs with sub-repo routing and added
workspace repo discovery.
* **Bug Fixes**
* Persisted per-workspace worktree mappings reliably across updates and
store restarts.
* Prevented concurrent workspace tasks from colliding when registering
active sessions.
* Improved database upgrades via a new SQLite schema column and
migration.
* **Tests**
* Added regression coverage for workspace worktree persistence and
concurrent session registration.
* **Documentation**
* Documented the “active-session lease” concept and the prior
persistence issue.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2026-06-24 15:59:07 -07:00
gsxdsm
28ceca2cbd Address PR review feedback (#1747)
- core/store: include workspaceWorktrees in the slim and activity-log-limited
  SELECT lists (rowToTask reads it, but the explicit column lists omitted it, so
  slim/limited reads dropped the field and could misclassify workspace tasks);
  add regression tests for both read surfaces
- dashboard/register-git-github: validate caller-supplied repoPath in resolveGitDir
  via isPathWithin containment check (path-traversal hardening for all git
  endpoints); make loadWorkspaceConfig a static @fusion/core import per AGENTS.md
- dashboard/legacy: preserve repoPath in the string-form pullBranch overload
- dashboard/GitManagerModal: revalidate selectedRepo against the fetched repo list
  so a stale selection can't persist across project switches

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-24 15:43:12 -07:00
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
2229e25e20 Merge branch 'main' into feature/refactor-apptsx 2026-06-24 11:49:37 -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