Without --foreground, `executor web` just opens whatever background service is already running, which keeps the scope it was started with. A --scope passed here was silently ignored, so the user could land on a different workspace and wonder where their config went. Now it warns loudly and names the flag that actually applies the scope (`executor web --foreground --scope <dir>`). Adds an e2e scenario in cli-web-transition that asserts the warning names the scope dir and the --foreground flag instead of silently dropping it.
Unified · Split
| @@ -2352,6 +2352,19 @@ const webCommand = Command.make( · 2351 unmodified lines | ||
| 2352 | 2352 | ({ foreground, port, scope, hostname, allowedHost, authToken }) => |
| 2353 | 2353 | Effect.gen(function* () { |
| 2354 | 2354 | if (!foreground) { |
| 2355 | + // `--scope` can ONLY be honored by a foreground server we boot here. | |
| 2356 | + // Without `--foreground` we just open whatever background service is | |
| 2357 | + // already running, which uses the scope IT was started with — so a | |
| 2358 | + // `--scope` here would be silently ignored and the user could land on a | |
| 2359 | + // different workspace ("where did my config go?"). Say so loudly and | |
| 2360 | + // point at the flag that actually applies it. | |
| 2361 | + if (Option.isSome(scope)) { | |
| 2362 | + console.warn( | |
| 2363 | + `Ignoring --scope ${scope.value}: it only applies with --foreground. ` + | |
| 2364 | + `The running web app uses the scope it was started with. ` + | |
| 2365 | + `Run \`executor web --foreground --scope ${scope.value}\` to serve that workspace.`, | |
| 2366 | + ); | |
| 2367 | + } | |
| 2355 | 2368 | yield* openRunningLocalWebApp(); |
| 2356 | 2369 | return; |
| 2357 | 2370 | } |
| @@ -55,3 +55,35 @@ scenario( · 54 unmodified lines | ||
| 55 | 55 | } |
| 56 | 56 | }), |
| 57 | 57 | ); |
| 58 | + | |
| 59 | +scenario( | |
| 60 | + "CLI web · --scope without --foreground warns instead of silently ignoring it", | |
| 61 | + { timeout: 120_000 }, | |
| 62 | + Effect.promise(async () => { | |
| 63 | + // Regression guard for "Had to setup my config again": `executor web --scope X` | |
| 64 | + // without `--foreground` cannot honor the scope (it opens the running service, | |
| 65 | + // which keeps its own scope). That used to be a SILENT no-op — the user lands | |
| 66 | + // on a different workspace and thinks their config vanished. It must warn. | |
| 67 | + const root = mkdtempSync(join(tmpdir(), "executor-web-scope-")); | |
| 68 | + const dataDir = join(root, "data"); | |
| 69 | + const scopeDir = join(root, "workspace"); | |
| 70 | + try { | |
| 71 | + const { stdout, stderr } = await execFileAsync( | |
| 72 | + "bun", | |
| 73 | + ["run", "dev:cli", "web", "--scope", scopeDir], | |
| 74 | + { cwd: repoRoot, env: { ...process.env, EXECUTOR_DATA_DIR: dataDir } }, | |
| 75 | + ); | |
| 76 | + const output = `${stdout}\n${stderr}`; | |
| 77 | + | |
| 78 | + expect(output, "the dropped --scope is called out, not silently ignored").toContain( | |
| 79 | + "Ignoring --scope", | |
| 80 | + ); | |
| 81 | + expect(output, "the warning names the scope it dropped").toContain(scopeDir); | |
| 82 | + expect(output, "the warning points at the flag that actually applies scope").toContain( | |
| 83 | + "--foreground", | |
| 84 | + ); | |
| 85 | + } finally { | |
| 86 | + rmSync(root, { recursive: true, force: true }); | |
| 87 | + } | |
| 88 | + }), | |
| 89 | +); | |