Commit Graph

9878 Commits

Author SHA1 Message Date
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
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
5025e8ceb0 Merge branch 'main' into feature/refactor-apptsx 2026-06-24 10:51:17 -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