From acf3663ff81dba28fec03ee1b3e94fae8d8d993a Mon Sep 17 00:00:00 2001 From: Sawyer Date: Thu, 10 Sep 2026 16:02:26 -0700 Subject: [PATCH] Remove the inert --force CLI flag The flag was parsed into Config but never changed resume or run behavior. Reject it as unrecognized and drop the dead field. --- CHANGELOG.md | 9 ++- docs/ARCHITECTURE.md | 2 +- docs/IMPLEMENTATION.md | 3 +- docs/TUI.md | 3 +- scripts/eval-capability.test.ts | 1 - scripts/eval-capability.ts | 3 +- scripts/eval-public-swe-one.ts | 1 - src/config.test.ts | 66 ++++++++++--------- src/config/index.ts | 12 +--- src/tui/onboarding.ts | 1 - .../crash-run/simulate-exec-signal.ts | 1 - tests/unit/exec/runner.test.ts | 1 - 12 files changed, 46 insertions(+), 57 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 330712588..222ed4936 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,6 +13,11 @@ parallel copies under `docs/` or `scripts/notes/`. At cut time: rename ## [Unreleased] +### Removed + +- `--force` is no longer accepted. It had no runtime effect; resume and the + session picker already include failed and done sessions without it. + ## [0.3.20] - 2026-09-10 ### Added @@ -376,8 +381,8 @@ parallel copies under `docs/` or `scripts/notes/`. At cut time: rename reply. `send_input` steers only an in-flight running turn. Closed workers stay closed. - `corbits resume` orders sessions by last persist. The picker shows the 10 - most recent sessions and type-to-filter narrows that list. Default rows are - running and cancelled; `--force` includes failed and done. + most recent sessions across all statuses and type-to-filter narrows that + list. ### TUI diff --git a/docs/ARCHITECTURE.md b/docs/ARCHITECTURE.md index fe1587aed..4d5466ab4 100644 --- a/docs/ARCHITECTURE.md +++ b/docs/ARCHITECTURE.md @@ -72,7 +72,7 @@ In TUI chat mode there is no completion gate — the session stays open across t - `--config ` replaces the global settings file as the provider source (useful for CI per-run injection). A provider must be defined in a settings file; there is no env fallback. - `settings.ts` owns the schema, validators (the per-repo file rejects credentials), file loaders, and the pure `resolveProvider` precedence function. - `providers.ts` defines the `ProviderCatalogEntry` type and helpers for building TUI provider lists; `profiles.ts` handles profile-level selection logic. -- `loadConfig` is async (it reads settings files). Parses a leading `exec`/`run` subcommand, flags `--cwd`, `--config`, `--provider`, `--model`, `--force`, `--dangerously-skip-permissions` (forces this process; TUI `/yolo` persists as the user-global default), `--auto` / `--no-auto` (auto mode defaults on); collects positional arguments as the optional initial task for the TUI or the required prompt for exec. +- `loadConfig` is async (it reads settings files). Parses a leading `exec`/`run` subcommand, flags `--cwd`, `--config`, `--provider`, `--model`, `--dangerously-skip-permissions` (forces this process; TUI `/yolo` persists as the user-global default), `--auto` / `--no-auto` (auto mode defaults on); collects positional arguments as the optional initial task for the TUI or the required prompt for exec. - Both settings files are on the secret-guard denylist for path-keyed tools, so the agent cannot `read_file` its own credentials. Shell commands that reference them still require explicit operator approval. ### TUI Runner (`src/tui/runner.ts`) diff --git a/docs/IMPLEMENTATION.md b/docs/IMPLEMENTATION.md index c295f0cee..7a909429f 100644 --- a/docs/IMPLEMENTATION.md +++ b/docs/IMPLEMENTATION.md @@ -366,7 +366,7 @@ Printed by `corbits --help` / `-h` from `CLI_HELP_TEXT` in `src/config/index.ts` | -------------------------------- | -------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | _(no verb)_ | — | Interactive session; optional trailing task text | | `exec` / `run` | — | Run a prompt (non-interactive / one-shot) | -| `resume` / `continue` | — | Open the session picker for this folder (project-keyed to this checkout's git toplevel). Lists the 10 most recently persisted sessions, completed included. Type to filter. `--force` is not required to see finished threads. | +| `resume` / `continue` | — | Open the session picker for this folder (project-keyed to this checkout's git toplevel). Lists the 10 most recently persisted sessions, completed included. Type to filter. | | `--resume` | — | Open the interactive session picker | | `resume ` | — | Reopen a specific session | | `resume --pick` / `--list` | — | Interactive session picker | @@ -375,7 +375,6 @@ Printed by `corbits --help` / `-h` from `CLI_HELP_TEXT` in `src/config/index.ts` | `--provider ` | from settings | Select a configured provider | | `--model ` | provider default | Select a model for the active provider | | `--profile ` | — | Settings profile | -| `--force` | false | Override an existing run state | | `--dangerously-skip-permissions` | false | Auto-allow anything not denied by the authorization layer (gate + pre-gate workspace sandboxes; secret-guard / authz hard denies remain). This launch flag still forces this process; `/yolo [on\|off\|toggle]` persists as the user-global default via `setSkipPermissions` | | `--auto` | true (default) | Force auto mode on (workspace writes + unconstrained shell without prompts) | | `--no-auto` | false | Start with auto mode off (ask on every consequential action); no in-session key toggles it | diff --git a/docs/TUI.md b/docs/TUI.md index 3c79d8e88..21e54fd09 100644 --- a/docs/TUI.md +++ b/docs/TUI.md @@ -542,8 +542,7 @@ terminal's own text selection and copy work by default, with no Alt+M dance required. The resume picker lists the 10 most recently persisted sessions for this checkout — completed, failed, and crashed included. Recency is the last write to `run.json`, not start time. Type to filter by name -(printable keys claim the `>` row, same as the model picker); `--force` -is not a list filter. +(printable keys claim the `>` row, same as the model picker). ## The prompt box diff --git a/scripts/eval-capability.test.ts b/scripts/eval-capability.test.ts index 54de336bf..429033fb5 100644 --- a/scripts/eval-capability.test.ts +++ b/scripts/eval-capability.test.ts @@ -26,7 +26,6 @@ function sampleConfig(over: Partial = {}): Config { providerName: "openai", cwd: process.cwd(), task: "do it", - force: true, dangerouslySkipPermissions: true, skipPermissionsFromSettings: false, auto: false, diff --git a/scripts/eval-capability.ts b/scripts/eval-capability.ts index c82799d50..c8b59c712 100755 --- a/scripts/eval-capability.ts +++ b/scripts/eval-capability.ts @@ -601,7 +601,7 @@ async function resolveVariantLabels( probe.push("--provider", variant.provider); if (variant.model !== undefined) probe.push("--model", variant.model); if (opts.configPath !== undefined) probe.push("--config", opts.configPath); - probe.push("--force", "probe"); + probe.push("probe"); const cfg = await loadConfig(probe, { allowUnconfigured: true }); if (cfg.configured) { return { provider: cfg.providerName, model: cfg.model }; @@ -777,7 +777,6 @@ async function runCase( if (requested.model !== undefined) argv.push("--model", requested.model); if (opts.configPath !== undefined) argv.push("--config", opts.configPath); if (opts.skipPermissions) argv.push("--dangerously-skip-permissions"); - argv.push("--force"); if (opts.director !== undefined) argv.push("--director", opts.director); argv.push(prompt); diff --git a/scripts/eval-public-swe-one.ts b/scripts/eval-public-swe-one.ts index d08bff741..93166a4d5 100644 --- a/scripts/eval-public-swe-one.ts +++ b/scripts/eval-public-swe-one.ts @@ -403,7 +403,6 @@ async function main(): Promise { "--model", opts.model, "--dangerously-skip-permissions", - "--force", prompt, ]; diff --git a/src/config.test.ts b/src/config.test.ts index 5f5e66ce7..9fd350b49 100644 --- a/src/config.test.ts +++ b/src/config.test.ts @@ -135,7 +135,6 @@ describe("loadConfig", () => { expect(config.providerName).toBe("fireworks"); expect(config.globalSettingsPath).toBe(globalPath); expect(config.globalDefaultProvider).toBe("fireworks"); - expect(config.force).toBe(false); } finally { await rm(cwd, { recursive: true, force: true }); } @@ -450,16 +449,26 @@ describe("loadConfig", () => { } }); - test("parses --force", async () => { + test("rejects --force as unrecognized for tui, exec, and resume", async () => { const cwd = await emptyCwd(); try { const globalPath = await writeGlobalSettings(cwd); - const config = await loadConfig(["--cwd", cwd, "--force", "run task"], { - globalSettingsPath: globalPath, - }); - expect(config.force).toBe(true); - expect(config.task).toBe("run task"); - expect(config.command).toBe("tui"); + await expect( + loadConfig(["--cwd", cwd, "--force", "run task"], { + globalSettingsPath: globalPath, + }), + ).rejects.toThrow("unrecognized flag: --force"); + await expect( + loadConfig(["exec", "--cwd", cwd, "--force", "ship it"], { + globalSettingsPath: globalPath, + }), + ).rejects.toThrow("unrecognized flag: --force"); + const sessionId = generateSessionId(); + await expect( + loadConfig(["resume", sessionId, "--force", "--cwd", cwd], { + globalSettingsPath: globalPath, + }), + ).rejects.toThrow("unrecognized flag: --force"); } finally { await rm(cwd, { recursive: true, force: true }); } @@ -470,7 +479,7 @@ describe("loadConfig", () => { try { const globalPath = await writeGlobalSettings(cwd); const config = await loadConfig( - ["exec", "--cwd", cwd, "--force", "ship it"], + ["exec", "--cwd", cwd, "--auto", "ship it"], { globalSettingsPath: globalPath, }, @@ -478,7 +487,7 @@ describe("loadConfig", () => { assertConfigured(config); expect(config.command).toBe("exec"); expect(config.task).toBe("ship it"); - expect(config.force).toBe(true); + expect(config.auto).toBe(true); } finally { await rm(cwd, { recursive: true, force: true }); } @@ -654,7 +663,7 @@ describe("loadConfig", () => { } }); - test("resume --force reopens a failed session that recorded an error", async () => { + test("resume reopens a failed session that recorded an error", async () => { const cwd = await emptyCwd(); const home = await mkdtemp(join(tmpdir(), "ic-resume-home-")); try { @@ -674,26 +683,22 @@ describe("loadConfig", () => { }, home, ); - const config = await loadConfig( - ["resume", sessionId, "--force", "--cwd", cwd], - { - globalSettingsPath: globalPath, - home, - }, - ); + const config = await loadConfig(["resume", sessionId, "--cwd", cwd], { + globalSettingsPath: globalPath, + home, + }); assertConfigured(config); expect(config.resumeMode).toBe("id"); expect(config.sessionId).toBe(sessionId); expect(config.skipInitialTask).toBe(true); expect(config.task).toBe("ship resume after failure"); - expect(config.force).toBe(true); } finally { await rm(cwd, { recursive: true, force: true }); await rm(home, { recursive: true, force: true }); } }); - test("resume --force among failed siblings stays silent and reopens the target", async () => { + test("resume among failed siblings stays silent and reopens the target", async () => { const cwd = await emptyCwd(); const home = await mkdtemp(join(tmpdir(), "ic-resume-home-")); try { @@ -719,13 +724,10 @@ describe("loadConfig", () => { let config: Awaited> | undefined; const logged = await withFileLogSink(async () => { - config = await loadConfig( - ["resume", targetId, "--force", "--cwd", cwd], - { - globalSettingsPath: globalPath, - home, - }, - ); + config = await loadConfig(["resume", targetId, "--cwd", cwd], { + globalSettingsPath: globalPath, + home, + }); }); const loaded = defined(config, "config"); assertConfigured(loaded); @@ -843,7 +845,7 @@ describe("loadConfig", () => { let thrown: unknown; const logged = await withFileLogSink(async () => { try { - await loadConfig(["resume", sessionId, "--force", "--cwd", cwd], { + await loadConfig(["resume", sessionId, "--cwd", cwd], { globalSettingsPath: globalPath, home, }); @@ -923,8 +925,8 @@ describe("loadConfig", () => { }); test("--help after flags throws CliHelpError", async () => { - await expectCliHelp(["--force", "--help"]); - await expectCliHelp(["--force", "-h"]); + await expectCliHelp(["--auto", "--help"]); + await expectCliHelp(["--auto", "-h"]); }); test("--help after a positional throws CliHelpError", async () => { @@ -967,7 +969,7 @@ describe("loadConfig", () => { test("value flags reject other flag-shaped tokens as values", async () => { await expect( - loadConfig(["--provider", "--force"], { + loadConfig(["--provider", "--auto"], { globalSettingsPath: NO_SETTINGS, }), ).rejects.toThrow("--provider requires a value"); @@ -978,7 +980,7 @@ describe("loadConfig", () => { loadConfig(["--cwd", "--tmp"], { globalSettingsPath: NO_SETTINGS }), ).rejects.toThrow("--cwd requires a value"); await expect( - loadConfig(["exec", "--director", "--force", "ship it"], { + loadConfig(["exec", "--director", "--auto", "ship it"], { globalSettingsPath: NO_SETTINGS, }), ).rejects.toThrow("--director requires a value"); diff --git a/src/config/index.ts b/src/config/index.ts index 26272ab52..4e87be859 100644 --- a/src/config/index.ts +++ b/src/config/index.ts @@ -458,7 +458,6 @@ export interface Config { verified?: boolean; cwd: string; task: string; - force: boolean; dangerouslySkipPermissions: boolean; // True when dangerouslySkipPermissions came from the persisted global // default rather than this invocation's CLI flag. Entry points use this to @@ -529,7 +528,6 @@ export interface UnconfiguredConfig { configured: false; cwd: string; task: string; - force: boolean; dangerouslySkipPermissions: boolean; skipPermissionsFromSettings: boolean; auto: boolean; @@ -573,7 +571,6 @@ Flags: --model model for the active provider --profile settings profile --resume interactive session picker - --force override an existing run state --director exec-only: run as this director (default: skywalker) --dangerously-skip-permissions skip permission prompts for this run only; @@ -684,7 +681,6 @@ export async function loadConfig( } let cwd = process.cwd(); - let force = false; let dangerouslySkipPermissions = false; // Auto mode is the default: non-destructive consequential actions (file // writes/edits and unconstrained shell) run without prompting, while shell @@ -702,7 +698,7 @@ export async function loadConfig( const positional: string[] = []; const requireValue = (flag: string, value: string | undefined): string => { - // Flag-shaped tokens are never option values. `--provider --force` and a + // Flag-shaped tokens are never option values. `--provider --auto` and a // trailing `--provider` both surface as a missing value rather than binding // the next flag (or accepting `--help`, which is already handled above). if (value === undefined || isFlagToken(value)) { @@ -734,10 +730,6 @@ export async function loadConfig( profileFlag = requireValue("--profile", args[++i]); continue; } - if (arg === "--force") { - force = true; - continue; - } if (arg === "--director") { const value = requireValue("--director", args[++i]); if (command !== "exec") { @@ -914,7 +906,6 @@ export async function loadConfig( configured: false, cwd, task, - force, dangerouslySkipPermissions, skipPermissionsFromSettings, auto, @@ -984,7 +975,6 @@ export async function loadConfig( ...resolved, cwd, task: resumeTask, - force, dangerouslySkipPermissions, skipPermissionsFromSettings, auto, diff --git a/src/tui/onboarding.ts b/src/tui/onboarding.ts index e337c5124..0a86ed49d 100644 --- a/src/tui/onboarding.ts +++ b/src/tui/onboarding.ts @@ -73,7 +73,6 @@ export async function runOnboarding( argv.push("--config", config.cliConfigPath); if (config.dangerouslySkipPermissions) argv.push("--dangerously-skip-permissions"); - if (config.force) argv.push("--force"); if (config.task.length > 0) argv.push(config.task); // Preserve programmatic isolation independently of the path that won settings diff --git a/tests/fixtures/crash-run/simulate-exec-signal.ts b/tests/fixtures/crash-run/simulate-exec-signal.ts index 820aadd88..1f341245a 100644 --- a/tests/fixtures/crash-run/simulate-exec-signal.ts +++ b/tests/fixtures/crash-run/simulate-exec-signal.ts @@ -58,7 +58,6 @@ await withMockedModuleDuring( providerName: "test-provider", model: "test-model", providers: {}, - force: false, dangerouslySkipPermissions: true, autoMode: false, sessionId, diff --git a/tests/unit/exec/runner.test.ts b/tests/unit/exec/runner.test.ts index 8fa9dc2c4..1c16ed955 100644 --- a/tests/unit/exec/runner.test.ts +++ b/tests/unit/exec/runner.test.ts @@ -40,7 +40,6 @@ function bareConfig(task: string): Config { providerName: "test", model: "test", providers: {}, - force: false, dangerouslySkipPermissions: true, autoMode: false, sessionId: "test-session",