Commit Graph

4639 Commits

Author SHA1 Message Date
Aron Prins
a3ad698ea2 test(FN-0000): mock full agent detail API surface 2026-04-24 13:47:12 +02:00
Aron Prins
773fb0a5f2 test(FN-0000): update SSE bus URL expectations 2026-04-24 13:23:44 +02:00
Aron Prins
f77dd9defa fix(FN-0000): stabilize automation cron scheduling and SSE coverage 2026-04-24 12:51:53 +02:00
Aron Prins
0f7680a70d fix(FN-0000): prevent stale dashboard service worker reload traps 2026-04-24 11:38:20 +02:00
Aron Prins
51d1b78eeb fix(dashboard): close SSE EventSources on page unload to prevent freeze
After ~3 refreshes, the dashboard would hang on "Initializing dashboard..."
with all /api/* fetches stalling. Root cause: Chrome keeps HTTP/1.1 sockets
in its keep-alive pool across page navigations even after EventSource is
garbage-collected. Once 6 (the per-origin limit) are held, every new fetch
queues indefinitely and the app can't finish booting.

Fix, layered:

1. sse-bus.ts — pagehide/beforeunload listeners close all active channels
   and send a sendBeacon to /api/events/disconnect so the server forces
   the socket closed (socket.destroy) rather than waiting for the browser
   to notice. Uses a sessionStorage clientId to correlate.

2. api.ts — createResilientEventSource (used by planning / mission / slice
   stream endpoints) registers every handle in a module-level set and
   closes them all on pagehide/beforeunload. sse-bus doesn't see these
   streams, so it needs its own teardown.

3. sse.ts — server-side connection bookkeeping. Tracks managed SSE
   connections by clientId, supports client-triggered disconnect via
   POST /api/events/disconnect, stale-timer cleanup, and supersedes
   older streams when a client reconnects.

4. server.ts — exposes /api/events/disconnect and /api/events/keepalive
   under a dedicated 300 req/min rate limit.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-24 11:09:29 +02:00
gsxdsm
8c3aef9526 feat(FN-2399): merge fusion/fn-2399 2026-04-24 00:43:15 -07:00
gsxdsm
2f76dea35f chore(release): v0.1.2
Version bump via changesets.
2026-04-24 00:39:37 -07:00
Fusion
11c04f0c43 fix(FN-2355): improve AI route diagnostics and stabilize related tests
- Add structured ai-session diagnostics for summarize-title and agent-generation error paths in dashboard routes
- Emit debug-gated summarize request/model resolution diagnostics when FUSION_DEBUG_AI is enabled
- Add route tests that assert diagnostics payloads for summarize and agent generation failures
- Reduce test flakiness by increasing core Vitest timeouts and relaxing brittle extension-discovery argument matching
2026-04-24 00:21:23 -07:00
gsxdsm
d77c6e198c perf(test): halve vitest workers to avoid workspace-concurrency oversubscription
Reduce VITEST_MAX_WORKERS from 4 to 2 in root `test` script so the
2-package workspace-concurrency fan-out yields 4 total threads instead
of 8, eliminating nondeterministic @fusion/core timeouts under load.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-24 00:20:41 -07:00
Fusion
9c47ed07d7 fix(FN-2316): harden pi session message normalization 2026-04-24 00:20:41 -07:00
gsxdsm
49a5605454 fix(FN-2316): harden pi session message normalization 2026-04-23 23:38:05 -07:00
gsxdsm
6feb697041 fix(FN-000): stabilize failing tests and trim slow waits 2026-04-23 23:11:58 -07:00
gsxdsm
9bf2981b22 feat(FN-2340): merge fusion/fn-2340 2026-04-23 23:02:38 -07:00
gsxdsm
9ddb475947 perf(dashboard): hoist agent-store setup to beforeAll in routes.test
The PATCH /tasks/:id/assign and Task checkout routes describe blocks
re-initialized a real AgentStore (sqlite init + createAgent) in their
beforeEach hooks, even though no test in either block mutates the agent
rows. Moving the agent setup to beforeAll while keeping the per-test
mock-store reset cuts ~20 sqlite init cycles across these two blocks.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-23 23:02:38 -07:00
gsxdsm
fcf47d2c77 perf(cli): skip native-binary build tests by default
The build-exe and build-exe-cross suites invoke `bun build` in beforeAll
hooks and pegged CPU for ~6 min on every test run (one cached-on-miss
build plus three unconditional cross-target builds). Move them to a
dedicated vitest project, skip them unless CI=1 or FUSION_TEST_BUILD_EXE=1
is set, and add cache guards so CI re-runs skip rebuilds when the target
binary already exists. With build-exe out of the default cli run, the
main config can enable fileParallelism.

Run on demand via `pnpm --filter @runfusion/fusion test:build-exe`.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-23 23:02:38 -07:00
gsxdsm
5f7cba63d6 feat(claude-cli): add Claude Opus 4.7 to the provider model list
The pinned @mariozechner/pi-ai catalog tops out at Opus 4.6 / Sonnet 4.6
/ Haiku 4.5. Append a Claude Opus 4.7 entry (1M ctx, 128k max out,
$5/$25 per MTok) with id-dedupe so it becomes a no-op once the upstream
catalog catches up. Sonnet 4.6 and Haiku 4.5 remain current per
https://platform.claude.com/docs/en/about-claude/models/overview and
are already in the catalog, so no other additions are needed.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-23 23:02:38 -07:00
gsxdsm
8f5c60fd8c refactor(claude-cli): always load extension, gate via /api/models filter
Previously the vendored @fusion/pi-claude-cli extension was conditionally
loaded based on GlobalSettings.useClaudeCli. That forced a Fusion restart
every time the user toggled the provider card — confusing UX.

Key insight: pi-claude-cli registers a NEW provider id ("pi-claude-cli")
rather than overriding "anthropic", so loading it unconditionally is
safe — direct Anthropic auth and CLI-routed models coexist peacefully.
The extension also gracefully no-ops when the `claude` binary is missing
(see packages/pi-claude-cli/index.ts:106 — the throw is caught locally).

Changes:
 - serve/daemon/dashboard: always append the resolved pi-claude-cli path
   to discoverAndLoadExtensions, no settings lookup.
 - resolveClaudeCliExtensionPaths() takes no args now; always returns the
   resolved path.
 - /api/models filter flipped: hide provider === "pi-claude-cli" when
   the toggle is OFF (previously: restricted to those models when ON).
 - POST /api/auth/claude-cli drops restartRequired semantics — toggling
   now has immediate effect on the picker.
 - Provider card UX updated to match: "Claude-CLI-routed models are
   now visible/hidden from the model picker" instead of "Restart Fusion
   to activate".

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-23 23:02:37 -07:00
gsxdsm
b354e49940 fix(dashboard): bump in-memory rate limits to avoid spurious 429s
The shared per-IP mutation bucket (30/min) made common dashboard actions
like Respecify fail with "Too many requests" after light activity. Raise
mutation to 600/min, api to 1000/min, sse to 60/min for the local-first
use case.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-23 23:02:37 -07:00
gsxdsm
3a39ad66b2 fix(core): use query param instead of hash fragment for plugin cache-busting
Vite/Vitest's resolver treats `#` as part of the filesystem path in some
environments, causing ERR_MODULE_NOT_FOUND on plugin reload.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-23 23:02:37 -07:00
gsxdsm
c36d16e87d Revert "perf(test): parallelize harder — unlock worker count, split build-output, bump workspace concurrency"
This reverts commit 972ad0f9fa55dc1419660aba4758a9d3c325919a.
2026-04-23 23:02:37 -07:00
Fusion
6ca8a04544 feat(FN-2369): merge fusion/fn-2369 2026-04-23 23:02:37 -07:00
Fusion
d62bf8e24c feat(prompts): require lint alongside tests and typecheck in agent instructions
Every agent-facing quality gate that used to pair tests with typecheck now
also includes lint. Specifically:

- core/src/types.ts: QA Check skill prompt runs lint, tests, typecheck (was
  tests only) and gates task_done() on all three.
- core/src/agent-prompts.ts + engine/src/reviewer.ts: "Do NOT issue REVISE"
  exclusion list now covers lint as well, so out-of-scope fixes that
  restore lint remain allowed (matches the already-lint-aware completion
  gate text at the top of the same prompts).
- engine/src/executor.ts: task_done() pre-flight checklist adds an explicit
  "if the repo has a lint command, run it and fix failures" bullet, mirrors
  the typecheck bullet, and expands the CRITICAL line from "ALL test
  failures" to "ALL lint, test, and typecheck failures".
- core/src/store.ts: default Step 2 checklist (Testing & Verification) now
  includes Lint and Typecheck alongside "All tests pass".
- cli/src/commands/plugin-scaffold.ts: generated plugin README and the
  "Next steps" CLI output include \`pnpm lint\` between install and test.

Existing prompts that already paired lint with tests+typecheck (the
Completion section, hard quality gates, triage testing requirements) are
unchanged.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-23 23:02:37 -07:00
gsxdsm
8def13ee2e perf(test): parallelize harder — unlock worker count, split build-output, bump workspace concurrency
- All 8 vitest configs now default maxWorkers to \`cpus().length - 1\`
  instead of the arbitrary \`Math.min(4, …)\` cap. Respects an explicit
  VITEST_MAX_WORKERS override for constrained environments (CI, laptops
  on battery). CLI keeps \`fileParallelism: false\` — audit found real
  shared state (process.chdir in agent-import, dist/ races in build-exe
  suites) that needs refactoring before we can flip it.
- packages/dashboard: move the mobile build-output smoke test (which
  invokes \`pnpm build:client\` via execSync, ~3s per run) into a
  dedicated \`test:build\` script so \`pnpm test\` isn't gated by it. A
  matching root script keeps CI wiring simple.
- Root: bump --workspace-concurrency from 2 → 4 so core/engine/cli/
  desktop can pipeline against dashboard's tail.
- Drop the now-redundant VITEST_MAX_WORKERS=4 prefix from the root
  scripts; the per-package configs pick up cpu count themselves.

Dashboard test suite: 182s → 38s (5x) on a 10-core machine. Full
workspace run: ~3m15 → 2m29. All tests still pass.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-23 23:02:37 -07:00
gsxdsm
9113f51f69 fix(core): recognize legacy kb-* backups and canonicalize .kb/backups settings
- BackupManager.listBackups now matches kb-* and kb-pre-restore-* filenames
  alongside the fusion-* pattern, parsing timestamps from either prefix.
- canonicalizeSettings rewrites autoBackupDir: ".kb/backups" to
  ".fusion/backups" so projects upgraded from the old brand keep working
  (custom .kb/* paths remain untouched).
- createBackupManager applies the same canonicalization to settings it
  receives, so the factory path also produces backups under .fusion/backups.
- Re-export getErrorMessage from core/src/types.ts so the dashboard's vite
  "@fusion/core" alias (which points at types.ts) resolves the symbol for
  client-side consumers — fixes the mobile build-output test.

Clears all 8 pre-existing kb → fn rename failures plus the 1 test that
regressed from the new getErrorMessage import surfacing the vite alias gap.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-23 23:02:37 -07:00
gsxdsm
4101af8547 refactor: eliminate remaining 15 any warnings and ratchet rule to error
- TaskCard: four catch((err: any) => err.message) promise handlers in
  archive/unarchive/delete/move → catch((err) => getErrorMessage(err)).
- InlineCreateCard + QuickEntryBox: .catch((err: any)) model-load handlers
  → getErrorMessage(err) with existing @fusion/core import.
- TerminalModal: drop (navigator as any).maxTouchPoints — modern lib.dom
  types already expose the property.
- serve.ts: remove unused any annotation on OpenRouter model mapper; the
  array element type is already inferred from json.data.
- pi.js, runtime-resolution.ts, dashboard.ts, serve.ts, dev-server-port-
  detect.ts, devserver-manager.ts: drop now-stale eslint-disable comments
  that the cleanup made redundant.

Fix a prompt-builder regression surfaced by agent's `any` cleanup: toolCall
with a raw string `arguments` field must be preserved verbatim (JSON-quoted)
rather than coerced to `{}`; restores a previously-passing test.

Then promote @typescript-eslint/no-explicit-any from warn → error. Future
new anys must either come with a one-line disable + justification or use a
real type. Workspace is now lint-clean (0 problems).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-23 23:02:37 -07:00
gsxdsm
4cabe7f613 refactor: eliminate ~400 no-explicit-any warnings across the workspace
Parallel subagent pass: four typescript-pro agents on non-overlapping scopes.

Patterns applied:
- catch (err: any) { ... err.message ... } → catch (err) { ... getErrorMessage(err) ... }
  using the new @fusion/core helper. Bare catch {} where the error was unused.
- SQLite row types: defined typed XxxRow interfaces per table and cast
  .all()/.get() results via `as unknown as XxxRow[]` (the double cast is
  required because better-sqlite3 returns Record<string, SQLOutputValue>).
- rowToX(row: any) converters: typed argument with the matching row interface.
- Dynamic settings key writes: (settings as Record<string, unknown>)[key].
- React event handlers and setState callbacks: inferred types or concrete
  React.{Mouse,Change,Form}Event<...> where needed.
- pi-claude-cli: local PiMessage / PiContext duck types to avoid re-typing
  pi-ai concrete shapes; typed Claude stream event message fields.

72 files changed, ~400 anys eliminated. Typecheck passes across the workspace.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-23 23:02:36 -07:00
gsxdsm
d1bd02b2c9 feat(core): add getErrorMessage helper for narrowing unknown errors
Designed to replace the \`catch (err: any) { ... err.message ... }\` pattern
across the repo. Keeps the catch binding typed as \`unknown\` (TS default)
while still producing a readable message.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-23 23:02:36 -07:00
gsxdsm
651c5678d2 refactor: fix and tighten mechanical lint rules
- no-useless-escape: drop needless backslashes in character classes and
  URL/path regexes (gh-cli, store, task, modelFilter, useFileMention,
  RoutineEditor, ScheduleForm).
- no-case-declarations: wrap case bodies in ProjectOverview and
  SettingsModal with block scopes.
- prefer-const: convert a never-reassigned slug binding in agent-import;
  annotate legitimate forward-declared let bindings in dashboard.ts that
  callbacks close over before assignment.
- no-fallthrough: add missing break after settings-subcommand error.
- no-empty-interface/no-empty-object-type: convert ProjectManifest from
  empty interface extension to a type alias.
- no-unused-expressions: replace `x && x.method()` short-circuits in
  TerminalModal with optional chaining.

Then ratchet these rules from warn → error so regressions are blocked.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-23 23:02:36 -07:00
gsxdsm
ce167b6397 chore(eslint): fix pre-existing errors surfaced by wider .cjs match
- Widen config match from top-level *.cjs to **/*.cjs so nested CommonJS
  scripts (e.g. mcp-schema-server.cjs) get Node globals and require() allowance.
- Replace lazy require() in claude-skills.ts with a normal top-level fs import.
- Tighten an any-typed tool map in pi-claude-cli to { name: string }.

Clears the remaining 12 lint errors; workspace now has 0 errors, 450 warnings.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-23 23:02:36 -07:00
gsxdsm
30d35b101e chore(eslint): promote @typescript-eslint/no-unused-vars from warn to error
Codebase is now clean for this rule, so enforcing it as an error prevents
regressions. Intentionally unused bindings remain exempt via the \`^_\` prefix
convention already documented in the rule options.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-23 23:02:35 -07:00
gsxdsm
cfa444700f refactor(dashboard,desktop,engine): remove unused imports, props, and locals
Clears the remaining no-unused-vars warnings across the dashboard app and
server, desktop main, and engine sources. Dead React state destructures are
collapsed to setter-only, unused props are underscore-prefixed to preserve
API shape, and unreferenced catch bindings are dropped. No behaviour change.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-23 23:02:35 -07:00
gsxdsm
b818aa2196 refactor(core): remove unused imports, helpers, and dead migration constant
Drops orphaned imports and the never-referenced V4 migration SQL constant
(V4 was inlined into runMigrations). Also drops the unused TypedEventEmitter
helper type and unused destructured values from stores.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-23 23:02:35 -07:00
gsxdsm
90d8dfaef2 refactor(cli): remove unused imports and variables
Eliminates no-unused-vars warnings across the CLI package by dropping
dead type imports, unused destructured helpers, and simplifying try/catch
blocks whose caught errors and intermediate results were never read.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-23 23:02:35 -07:00
gsxdsm
6296c3cb53 refactor: adapt resource loader and tool wiring to pi-coding-agent 0.70
Three API shifts converge here:

- DefaultResourceLoaderOptions.agentDir is required as of 0.68 (the
  old process.cwd() fallback was removed). Pass getFusionAgentDir()
  explicitly in createFnAgent.

- createAgentSession({ tools }) is now a string[] allowlist of tool
  names, not a Tool[] array (0.68). Our boundary-wrapping via
  wrapToolsWithBoundary produces Tool instances, so we can no longer
  pass them through \`tools\`. Move them into \`customTools\` and
  suppress the built-in defaults with \`noTools: "builtin"\`. The
  wrapped tools keep the same names (read, bash, ...) as the built-ins
  they replace, so no call-site or prompt changes are needed.

- SettingsManager.create's first arg (cwd) became required (was
  optional before). Dashboard routes that previously passed
  \`undefined\` for a process-global settings view now pass
  process.cwd() to match the existing DefaultPackageManager call below.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-23 23:02:35 -07:00
gsxdsm
4ce0bf57ac fix: adapt to AgentState.error → errorMessage rename
pi-coding-agent 0.70 replaced the mutable \`AgentState.error\` field with
a readonly \`AgentState.errorMessage\`. \`session.prompt()\` still does
not throw when retries are exhausted, so we still need to re-raise the
stored error after each prompt.

- checkSessionError (usage-limit-detector): widen parameter to accept
  either key; prefer errorMessage so new sessions work, fall back to
  error so we can deploy without forcing everyone's caches to rebuild.
- agent-reflection: same widening at the call site.
- pi.ts helpers: read both keys, best-effort clear both (the new field
  is readonly, so the write is a no-op on 0.70 sessions but still
  matters for mock sessions in tests).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-23 23:02:35 -07:00
gsxdsm
6a8568e77c refactor: migrate @sinclair/typebox imports to typebox 1.x
The pi-coding-agent SDK moved from @sinclair/typebox 0.34.x to the new
typebox 1.x package in 0.69. Our direct imports in merger.ts and
extension.ts need to follow so tool schemas resolve to the same TSchema
the SDK consumes.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-23 23:02:34 -07:00
gsxdsm
9cfd09c5d9 refactor: migrate to ModelRegistry.create factory
ModelRegistry's public constructor became private in pi-coding-agent 0.64.
Direct `new ModelRegistry(...)` calls no longer compile. Switch the five
production sites to the factory (`ModelRegistry.create`) and update the
four test modules that mocked the class as a constructor to now mock it
as an object with `create` and `inMemory` static methods.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-23 23:02:34 -07:00
gsxdsm
d8a1158d40 chore: bump pi-coding-agent + pi-ai to 0.70.0
Jumps engine, cli, and dashboard from 0.62.0 → 0.70.0. Replaces the
@sinclair/typebox 0.34.x dep with typebox@^1 (SDK migrated in 0.69).
Picks up eight releases of provider-side reliability fixes (Anthropic,
OpenAI Responses/Codex, Bedrock, OpenRouter, Kimi), Opus 4.7 adaptive
thinking support, and a uuid security bump. Code migrations follow in
subsequent commits.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-23 23:02:34 -07:00
gsxdsm
f20d0b5a18 refactor: remove legacy kb compatibility
Drops the .kb/kb.db migration path, legacy backup filename handling, and
backward-compat test suites. Renames internal kbDir identifiers to
fusionDir and hasKbProject/isValidKbProject to their fusion equivalents.

- Remove needsCentralMigration, autoMigrateToCentral, and the
  "needs-migration" FirstRunState; checkAndMigrate and KB_SKIP_MIGRATION
  env var are gone
- Remove LEGACY_BACKUP_DIR and canonicalizeBackupDir; listBackups no
  longer matches kb-* filenames
- Delete backward-compat.test.ts and store-backward-compat.test.ts;
  update remaining tests to new 3-state first-run model

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-23 23:02:34 -07:00
gsxdsm
f4e0850f9d feat: add "Anthropic — via Claude CLI" as a first-class provider
Replaces the stray useClaudeCli settings checkbox + onboarding question
with a proper provider-card UX. The card lives next to OAuth + API-key
cards in onboarding and settings, with Enable/Disable + Test actions.

Backend:
 - Vendors rchern/pi-claude-cli@0.3.1 as packages/pi-claude-cli
   (MIT, attribution in UPSTREAM.md). Lets us bump peer-dep on
   pi-coding-agent in lockstep with Fusion (upstream pinned ^0.52.0
   vs ours ^0.62.0) and fix bugs without waiting on upstream.
 - Adds @fusion/pi-claude-cli as a workspace dep of @runfusion/fusion
   so users don't have to `npm install -g pi-claude-cli` manually.
 - serve/daemon/dashboard conditionally load the extension via
   discoverAndLoadExtensions() when GlobalSettings.useClaudeCli is on;
   no side-effects on user ~/.fusion/agent/settings.json.
 - New GET /api/providers/claude-cli/status: claude --version probe
   + toggle state + cached extension resolution.
 - New POST /api/auth/claude-cli: flips useClaudeCli, refuses if the
   claude binary is missing, fires the existing skill-backfill hook.
 - /api/auth/status now injects a synthetic {id:"claude-cli", type:"cli"}
   provider entry so onboarding + settings see a consistent list.

Frontend:
 - New ClaudeCliProviderCard component shared between ModelOnboardingModal
   and SettingsModal's Authentication section.
 - New AuthProvider.type = "cli" variant.
 - Removed the old "Route AI calls through the Claude CLI" checkbox from
   Global Models settings and the opt-in step from the onboarding wizard.
 - ProviderIcon gets a composite Anthropic-mark-plus-terminal glyph for
   the claude-cli provider id.

Tests:
 - 8 unit tests for extension resolution (@fusion/pi-claude-cli is
   workspace-linked so these run in-tree).
 - 2 unit tests for the binary probe.
 - Existing /auth/status tests filter out the new synthetic entry so
   they keep asserting structural OAuth/API-key behavior in isolation.
 - The vendored package's own 296 tests still pass unchanged.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-23 23:02:34 -07:00
Fusion
82b4c696d8 test(FN-2358): harden clean-worktree CI verification tests
- Assert contributing docs explicitly state that pnpm test must run without prior build outputs
- Add CI workflow test coverage to keep docs and verify:workspace ordering aligned
- Expand Vitest workspace alias assertions to include @fusion/test-utils and src-only replacements
- Validate real symbol imports from workspace packages when dist directories are absent
2026-04-23 23:02:34 -07:00
Fusion
58b69e7310 fix(FN-2352): add structured terminal websocket diagnostics
- Add terminal websocket diagnostics helper for consistent lifecycle and error logging
- Redact sensitive websocket context fields before emitting diagnostics events
- Integrate diagnostics logging into dashboard server terminal websocket handling
- Expand server tests to assert structured diagnostics payloads and error context
2026-04-23 23:02:34 -07:00
gsxdsm
c250470b94 fix: use live merge-base for task diff scope
The in-progress/in-review Changes tab was inflating file counts by
preferring a stale task.baseCommitSha over the live merge-base with the
base branch. Once upstream commits are merged into a feature branch,
baseCommitSha..HEAD includes every upstream file as well, producing
counts far larger than the branch's own changes.

resolveDiffBase now prefers merge-base(HEAD, [origin/]baseBranch), falling
back to baseCommitSha only when no merge-base is available or when the
merge-base equals HEAD (task sitting on the base branch with no
divergence, e.g. unit-test scenarios).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-23 23:02:34 -07:00
gsxdsm
e8895c76b8 feat: backfill Claude skills when useClaudeCli toggle flips on
PUT /api/settings/global now fires an onUseClaudeCliToggled hook on an
actual transition so the UI toggle has immediate effect — serve/daemon/
dashboard wire it to ensureClaudeSkillsForAllProjectsOnStartup so every
registered project picks up .claude/skills/fusion without a restart.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-23 23:02:34 -07:00
Fusion
51870ed27b fix: prevent nested .fusion/.fusion dir from PluginStore path bug
PluginStore's constructor treats its rootDir arg as a project root and
internally appends `.fusion` before opening the SQLite DB. Several CLI
call sites were passing the already-resolved `.fusion` directory,
producing a doubled `.fusion/.fusion/fusion.db` that the dashboard
process kept recreating on every project load.

Pass the project root instead so the DB lands in the canonical
`.fusion/fusion.db` alongside the rest of the project's state.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-23 23:02:33 -07:00
gsxdsm
a1b2d48986 fix: prevent nested .fusion/.fusion dir from PluginStore path bug
PluginStore's constructor treats its rootDir arg as a project root and
internally appends `.fusion` before opening the SQLite DB. Several CLI
call sites were passing the already-resolved `.fusion` directory,
producing a doubled `.fusion/.fusion/fusion.db` that the dashboard
process kept recreating on every project load.

Pass the project root instead so the DB lands in the canonical
`.fusion/fusion.db` alongside the rest of the project's state.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-23 15:55:04 -07:00
Fusion
fbe5b82ce0 feat(FN-2361): codify workspace verification bootstrap contract
- Add root verify:workspace script enforcing lint -> test -> build order
- Update CI workflow to run verify:workspace as the single workspace gate before binary packaging
- Add CLI guardrail tests for workflow sequencing and root script contract invariants
- Document deterministic workspace bootstrap expectations in contributing guide
- Clarify bundle-output test bootstrap intent for explicit artifact setup
2026-04-23 15:53:47 -07:00
Fusion
54843012bc fix(FN-2360): harden CLI vitest workspace source aliasing
- Convert internal @fusion workspace aliases in packages/cli/vitest.config.ts to exact anchored regex entrypoint mappings
- Preserve subpath-before-root alias order so @fusion/core/gh-cli and @fusion/dashboard/planning resolve correctly
- Add vitest-workspace-resolution regression coverage for alias definitions and ordering
- Simulate clean worktrees by temporarily hiding internal dist/ directories and verify dynamic imports resolve from source
2026-04-23 15:31:37 -07:00
gsxdsm
5454408713 feat(FN-2363): merge fusion/fn-2363 2026-04-23 14:55:57 -07:00
Fusion
824d678b9a fix(FN-2350): harden agent-generation session disposal diagnostics
- Use shared nonfatal diagnostics wrapper when disposing agent-generation AI sessions
- Emit structured error context (sessionId and operation) instead of silently swallowing dispose failures
- Add regression coverage verifying generation succeeds while dispose failures are logged as nonfatal diagnostics
2026-04-23 14:53:49 -07:00