FN-5812: add onboard autolaunch backcompat guard coverage

Protect onboarding autolaunch behavior so legacy projects and agent-run commands are not blocked.

- add end-to-end CLI test coverage for onboarding autolaunch backcompat guard behavior
- document the onboarding autolaunch compatibility behavior in the CLI reference
- add a patch changeset for @runfusion/fusion release tracking

Files changed:
 .changeset/fn-5812-onboard-backcompat-guard.md     |   5 +
 docs/cli-reference.md                              |   6 +
 packages/cli/src/commands/__tests__/onboard-autolaunch-backcompat-e2e.test.ts      | 161 +++++++++++++++++++++
 3 files changed, 172 insertions(+)

Fusion-Task-Id: FN-5812

Fusion-Task-Lineage: d735ef0e-1abc-4c22-bd11-07c2de69ace1
This commit is contained in:
gsxdsm
2026-06-01 16:55:56 -07:00
parent 8376781864
commit 245129e24c
3 changed files with 172 additions and 0 deletions

View File

@@ -0,0 +1,161 @@
import { afterEach, describe, expect, it, vi } from "vitest";
import { maybeAutoLaunchOnboarding } from "../onboard-autolaunch.js";
describe("maybeAutoLaunchOnboarding backward-compat e2e guard", () => {
afterEach(() => {
vi.restoreAllMocks();
});
it("does not prompt onboarded interactive users with central and project DB present", async () => {
const runOnboard = vi.fn();
const pathExists = vi.fn((path: string) =>
path === "/virtual/central.db" || path.endsWith("/.fusion/fusion.db"),
);
await expect(
maybeAutoLaunchOnboarding({
command: "task",
args: ["task", "list"],
centralDbPath: "/virtual/central.db",
cwd: "/repo/demo",
isTTY: true,
pathExists,
runOnboard,
}),
).resolves.toBeUndefined();
expect(runOnboard).not.toHaveBeenCalled();
});
it("never blocks agent-run non-TTY task commands when nothing is initialized", async () => {
const runOnboard = vi.fn();
await expect(
maybeAutoLaunchOnboarding({
command: "task",
args: ["task", "list"],
centralDbPath: "/virtual/central.db",
isTTY: false,
pathExists: () => false,
runOnboard,
}),
).resolves.toBeUndefined();
expect(runOnboard).not.toHaveBeenCalled();
});
it("skips serve and daemon commands even with missing DB", async () => {
for (const command of ["serve", "daemon"]) {
const runOnboard = vi.fn();
await expect(
maybeAutoLaunchOnboarding({
command,
args: [command],
centralDbPath: "/virtual/central.db",
isTTY: true,
pathExists: () => false,
runOnboard,
}),
).resolves.toBeUndefined();
expect(runOnboard).not.toHaveBeenCalled();
}
});
it("honors --skip-onboarding end-to-end", async () => {
const runOnboard = vi.fn();
await expect(
maybeAutoLaunchOnboarding({
command: "task",
args: ["task", "list", "--skip-onboarding"],
centralDbPath: "/virtual/central.db",
isTTY: true,
pathExists: () => false,
runOnboard,
}),
).resolves.toBeUndefined();
expect(runOnboard).not.toHaveBeenCalled();
});
it("honors FUSION_SKIP_ONBOARDING end-to-end", async () => {
const runOnboard = vi.fn();
await expect(
maybeAutoLaunchOnboarding({
command: "task",
args: ["task", "list"],
centralDbPath: "/virtual/central.db",
isTTY: true,
env: { FUSION_SKIP_ONBOARDING: "true" },
pathExists: () => false,
runOnboard,
}),
).resolves.toBeUndefined();
expect(runOnboard).not.toHaveBeenCalled();
});
it("isolates runOnboard failures as non-fatal with one diagnostic", async () => {
const runOnboard = vi.fn().mockRejectedValue(new Error("boom"));
const errorSpy = vi.spyOn(console, "error").mockImplementation(() => undefined);
await expect(
maybeAutoLaunchOnboarding({
command: "task",
args: ["task", "list"],
centralDbPath: "/virtual/central.db",
isTTY: true,
pathExists: () => false,
runOnboard,
}),
).resolves.toBeUndefined();
expect(errorSpy).toHaveBeenCalledTimes(1);
expect(errorSpy).toHaveBeenCalledWith(
expect.stringContaining("[onboard-autolaunch] non-fatal onboard launch failure: boom"),
);
});
it("launches onboarding once for legitimate first-run interactive commands", async () => {
const runOnboard = vi.fn();
await expect(
maybeAutoLaunchOnboarding({
command: "task",
args: ["task", "list"],
centralDbPath: "/virtual/central.db",
isTTY: true,
pathExists: () => false,
runOnboard,
}),
).resolves.toBeUndefined();
expect(runOnboard).toHaveBeenCalledTimes(1);
});
it("derives projectInitialized from cwd/pathExists seam without real filesystem", async () => {
const runOnboard = vi.fn();
const pathExists = vi.fn((path: string) =>
path === "/virtual/central.db" || path === "/workspace/demo/.fusion/fusion.db",
);
await expect(
maybeAutoLaunchOnboarding({
command: "task",
args: ["task", "list"],
centralDbPath: "/virtual/central.db",
cwd: "/workspace/demo",
isTTY: true,
pathExists,
runOnboard,
}),
).resolves.toBeUndefined();
expect(pathExists).toHaveBeenCalledWith("/workspace/demo/.fusion/fusion.db");
expect(runOnboard).not.toHaveBeenCalled();
});
});