Skip to content

Add option to skip bare mode (avoid try-then-fallback overhead) - #1029

Draft
acreeger wants to merge 4 commits into
mainfrom
feat/issue-1028__skip-bare-mode
Draft

Add option to skip bare mode (avoid try-then-fallback overhead)#1029
acreeger wants to merge 4 commits into
mainfrom
feat/issue-1028__skip-bare-mode

Conversation

@acreeger

@acreeger acreeger commented Jul 9, 2026

Copy link
Copy Markdown
Collaborator

Fixes #1028

Add option to skip bare mode (avoid try-then-fallback overhead)

Issue details

Summary

Requested by @NoahCardoza in #1022.

For accounts where bare mode is never compatible (e.g. Enterprise Accounts), iloom currently attempts bare mode first and falls back on every start / commit / finish. This wastes time on each operation trying something that will always fail for these users.

Proposal

Add a configuration option to skip bare mode entirely, so bare mode is never attempted and iloom goes straight to the working path. This avoids the per-operation try-then-fallback overhead.

Context

I totally forgot to mention but a nice feature for my case could be the ability to skip bare mode. That way time isn't wasted every start/commit/finish to try bare mode first and fallback.

@NoahCardoza (comment)

Follow-up to #1022, which added the graceful fallback (shipped in v0.14.2). This issue tracks letting users opt out of the bare-mode attempt altogether.


This PR was created automatically by iloom.

@acreeger

acreeger commented Jul 9, 2026

Copy link
Copy Markdown
Collaborator Author

Analysis: Skip Bare Mode Option (#1028)

Problem Statement

When Claude CLI's --bare flag is used for headless utility operations (commit messages, branch names, session summaries, issue enhancement), iloom first attempts bare mode and falls back to non-bare mode on auth failure. For Enterprise accounts where --bare always fails (OAuth-only, no ANTHROPIC_API_KEY), every headless operation pays a try-then-fail overhead on every invocation.

Root Cause

The bare mode auto-apply logic lives in launchClaude() at src/utils/claude.ts:170:

// Auto-apply bare mode for headless utility operations when not explicitly set
if (bare === undefined && headless && noSessionPersistence) {
    const config = await resolveBareModeConfig()
    effectiveBare = config.bare
    effectiveSettings ??= config.settings
    oauthToken = config.oauthToken
    bareModeAutoApplied = config.bare
}

When bare mode is auto-applied and fails, the retry loop (lines 407–468) retries without --bare, wasting one full Claude CLI invocation cycle per operation.

The decision function resolveBareModeConfig() (line 671) resolves bare mode based on:

  1. ANTHROPIC_API_KEY env var present → { bare: true }
  2. OAuth token available → { bare: true, settings, oauthToken }
  3. Neither available → { bare: false }

For Enterprise accounts with OAuth tokens, path 2 triggers bare mode, but --bare may not be supported by their Claude CLI configuration, causing the consistent failure-then-fallback pattern described in the issue.

Affected Components

Primary: src/utils/claude.ts

  • resolveBareModeConfig() (line 671–690): The decision point. Needs to respect a "skip bare mode" setting. Currently has no access to iloom settings.
  • launchClaude() (line 156–468): Auto-apply logic at line 170 and retry loop at line 407. When skipBareMode is true, the auto-apply block should be skipped entirely.
  • ClaudeCliOptions interface (line 58–88): Needs a skipBareMode option so callers can pass the setting value.

Primary: src/lib/SettingsManager.ts

  • IloomSettingsSchema (line 482): Add skipBareMode boolean setting (default false).
  • IloomSettingsSchemaNoDefaults (line 789): Add matching optional skipBareMode field.

Callers that trigger bare mode auto-application

These are all services that call launchClaude() with { headless: true, noSessionPersistence: true }, triggering the auto-apply:

File Line Operation Settings Access
src/lib/CommitManager.ts 364, 372 Commit message generation Has getLogger(), needs settings
src/lib/SessionSummaryService.ts 89–96, 279–281 Session summary generation Constructor-injected
src/lib/IssueEnhancementService.ts 114–118, 254–258 Issue enhancement Constructor-injected
src/lib/ValidationRunner.ts 416–419 Validation (jsonStream mode) Receives settings
src/utils/claude.ts 743 generateBranchName() Standalone function

Documentation

  • docs/iloom-commands.md: Document the new skipBareMode setting.

Design Considerations

Approach: Settings-driven with ClaudeCliOptions passthrough

The cleanest approach adds skipBareMode at two levels:

  1. Settings schema (IloomSettingsSchema.skipBareMode): Users set this once in their .iloom/settings.json or ~/.config/iloom-ai/settings.json.

  2. ClaudeCliOptions.skipBareMode: Callers that have settings access pass this through. launchClaude() checks it before entering the auto-apply block.

  3. resolveBareModeConfig(): Accepts an optional { skipBareMode?: boolean } parameter — if true, returns { bare: false } immediately.

Why not an environment variable?

An env var would be simpler (no caller changes), but goes against the existing settings pattern. All other iloom configuration uses the SettingsManager hierarchy. Consistency matters.

Propagation to callers

Most callers (CommitManager, SessionSummaryService, IssueEnhancementService, ValidationRunner) are services with constructor-injected dependencies. They already receive or can receive settings. The standalone generateBranchName() function is the exception — it would need either a parameter addition or access to settings.

Questions / Assumptions

# Question Assumed Answer
1 Setting name: skipBareMode vs disableBareMode vs bare.skip? skipBareMode — matches the issue title language and is a simple boolean at the top level
2 Should this also affect explicit bare: true calls? No — only affects the auto-apply path. If a caller explicitly sets bare: true, respect that.
3 Should there be a CLI flag --skip-bare-mode on individual commands? Not initially — this is a persistent config. Users set it once and forget. CLI flag can be added later if needed.
4 Telemetry: should we track when this setting is active? Yes — track in loom.created to understand adoption.

Files Requiring Changes (Summary)

  1. src/lib/SettingsManager.ts — Add skipBareMode to both schema variants
  2. src/utils/claude.ts — Add to ClaudeCliOptions, modify resolveBareModeConfig() and launchClaude() auto-apply logic
  3. src/lib/CommitManager.ts — Pass skipBareMode from settings
  4. src/lib/SessionSummaryService.ts — Pass skipBareMode from settings
  5. src/lib/IssueEnhancementService.ts — Pass skipBareMode from settings
  6. src/lib/ValidationRunner.ts — Pass skipBareMode from settings
  7. docs/iloom-commands.md — Document the new setting
  8. Tests for the new behavior

@acreeger

acreeger commented Jul 9, 2026

Copy link
Copy Markdown
Collaborator Author

Implementation Plan: Skip Bare Mode Option (#1028)

Overview

Add a skipBareMode boolean setting so users can disable the bare-mode try-then-fallback overhead. When true, launchClaude() skips the auto-apply block entirely and never attempts --bare for headless utility operations.


Phase 1: Core Infrastructure (Settings + launchClaude)

Step 1: Add skipBareMode to settings schemas

File: src/lib/SettingsManager.ts

  1. IloomSettingsSchema (line ~537) — Add after copyGitIgnoredPatterns:
skipBareMode: z
    .boolean()
    .default(false)
    .describe(
        'Skip bare mode entirely for headless utility operations (commit messages, branch names, session summaries). ' +
        'Useful for Enterprise accounts where bare mode always fails due to OAuth-only authentication. ' +
        'When true, iloom goes straight to the non-bare code path, avoiding the try-then-fallback overhead.',
    ),
  1. IloomSettingsSchemaNoDefaults (line ~845) — Add after copyGitIgnoredPatterns:
skipBareMode: z
    .boolean()
    .optional()
    .describe(
        'Skip bare mode entirely for headless utility operations (commit messages, branch names, session summaries). ' +
        'Useful for Enterprise accounts where bare mode always fails due to OAuth-only authentication. ' +
        'When true, iloom goes straight to the non-bare code path, avoiding the try-then-fallback overhead.',
    ),

Step 2: Add skipBareMode to ClaudeCliOptions and modify launchClaude()

File: src/utils/claude.ts

  1. ClaudeCliOptions interface (line 87) — Add new field after settings:
skipBareMode?: boolean // Skip bare mode auto-apply for headless utility operations (user config passthrough)
  1. launchClaude() destructuring (line 160) — Add skipBareMode to the destructured options:
const { model, permissionMode, addDir, headless = false, systemPrompt, appendSystemPrompt, appendSystemPromptFile, mcpConfig, allowedTools, disallowedTools, agents, pluginDir, sessionId, noSessionPersistence, outputFormat, verbose, jsonMode, passthroughStdout, effort, env: extraEnv, signal, bare, settings, skipBareMode } = options
  1. Auto-apply guard (line 170) — Add skipBareMode check to the condition:
// Auto-apply bare mode for headless utility operations when not explicitly set
if (bare === undefined && headless && noSessionPersistence && !skipBareMode) {

Must-Haves (Phase 1)

  • skipBareMode appears in both IloomSettingsSchema (with .default(false)) and IloomSettingsSchemaNoDefaults (.optional())
  • ClaudeCliOptions has skipBareMode?: boolean
  • launchClaude() destructures skipBareMode and guards the auto-apply block with && !skipBareMode
  • pnpm build succeeds

Phase 2: Caller Propagation

All 5 callers that trigger bare mode auto-application need to pass skipBareMode from settings.

Step 3: CommitManager — Add settings access

File: src/lib/CommitManager.ts

CommitManager has no constructor-injected settings. The generateClaudeCommitMessage method (line 299) needs to accept skipBareMode as a parameter and pass it through to launchClaude.

  1. Method signature (line 299) — Add skipBareMode parameter:
public async generateClaudeCommitMessage(
    worktreePath: string,
    issueNumber: string | number | undefined,
    issuePrefix: string,
    trailerType?: 'Refs' | 'Fixes',
    skipBareMode?: boolean
): Promise<string | null> {
  1. Claude options object (line 359) — Add skipBareMode:
const claudeOptions = {
    headless: true,
    model: 'claude-haiku-4-5-20251001',
    timeout: 120000,
    systemPrompt: '...',
    noSessionPersistence: true,
    effort: 'low',
    skipBareMode,
}
  1. Update caller in src/commands/commit.ts — Find where generateClaudeCommitMessage is called and pass settings.skipBareMode:
    • Load settings via SettingsManager and pass settings.skipBareMode as the new parameter.

Step 4: SessionSummaryService — Pass through settings

File: src/lib/SessionSummaryService.ts

This service already has this.settingsManager and loads settings at line 135. The launchSessionSummary method (line 87) builds launchOptions — add skipBareMode.

  1. generateAndPostSummary method (line 122) — After loading settings at line 135, pass skipBareMode to launchSessionSummary:
const result = await this.launchSessionSummary(prompt, summaryModel, sessionId, settings.skipBareMode)
  1. launchSessionSummary method (line 87) — Add parameter and pass through:
private async launchSessionSummary(prompt: string, model: string, sessionId: string, skipBareMode?: boolean): Promise<string | void> {
    const launchOptions = {
        headless: true,
        model,
        sessionId,
        noSessionPersistence: true,
        skipBareMode,
    }
  1. Epic report method (line ~278) — The generateEpicReport also calls launchClaude with headless+noSessionPersistence. Pass skipBareMode there too:
const reportResult = await launchClaude(prompt, {
    headless: true,
    model: summaryModel,
    noSessionPersistence: true,
    skipBareMode: settings.skipBareMode,
})

Step 5: IssueEnhancementService — Pass through settings

File: src/lib/IssueEnhancementService.ts

This service already has this.settingsManager and loads settings at line 82 and 212.

  1. enhanceDescription method (line 114) — Add skipBareMode to the launchClaude call:
const enhanced = await launchClaude(prompt, {
    headless: true,
    model: 'sonnet',
    agents,
    noSessionPersistence: true,
    skipBareMode: settings.skipBareMode,
})
  1. Second launchClaude call (line 254) — Also add skipBareMode:
const response = await launchClaude(prompt, {
    headless: true,
    model: 'sonnet',
    agents,
    noSessionPersistence: true,
    skipBareMode: settings.skipBareMode,
    ...(mcpConfig && { mcpConfig }),
    ...(allowedTools && { allowedTools }),
    ...(disallowedTools && { disallowedTools }),
})

Step 6: ValidationRunner — Pass through settings

File: src/lib/ValidationRunner.ts

ValidationRunner's launchClaude call at line 414 uses headless: !!options.jsonStream — it only triggers bare mode when jsonStream is true. Still, pass skipBareMode for correctness.

  1. Need to check how ValidationRunner gets settings. It doesn't have settingsManager — it receives settings via the caller or constructs options. Add skipBareMode to the launchClaude options:
await launchClaude(prompt, {
    addDir: worktreePath,
    headless: !!options.jsonStream,
    permissionMode: options.jsonStream ? 'bypassPermissions' : 'acceptEdits',
    model: 'sonnet',
    noSessionPersistence: true,
    skipBareMode: options.skipBareMode,
    ...(options.jsonStream && { passthroughStdout: true }),
})
  1. Add skipBareMode?: boolean to the validation options type used by this method, and thread it from the caller.

Step 7: generateBranchName — Pass through

File: src/utils/claude.ts

generateBranchName (line 708) is a standalone function. Add an optional skipBareMode parameter:

  1. Function signature (line 708):
export async function generateBranchName(
    issueTitle: string,
    issueNumber: string | number,
    model: string = 'haiku',
    skipBareMode?: boolean
): Promise<string> {
  1. launchClaude call (line 743) — Add skipBareMode:
const result = (await launchClaude(prompt, {
    model,
    headless: true,
    noSessionPersistence: true,
    systemPrompt: '...',
    effort: 'low',
    skipBareMode,
})) as string
  1. ClaudeBranchNameStrategy (in src/lib/BranchNamingService.ts line 32) — Pass skipBareMode through from the caller. This requires threading settings, which may mean adding it to BranchGenerationOptions or to the ClaudeBranchNameStrategy constructor.

Must-Haves (Phase 2)

  • All 5 callers pass skipBareMode from their settings to launchClaude
  • CommitManager.generateClaudeCommitMessage accepts and forwards skipBareMode
  • generateBranchName() accepts and forwards skipBareMode
  • pnpm build succeeds
  • No bare mode auto-application occurs when skipBareMode: true is passed

Phase 3: Telemetry + Documentation + Tests

Step 8: Telemetry

File: src/types/telemetry.ts

Add skip_bare_mode to LoomCreatedProperties (line 25):

export interface LoomCreatedProperties {
    source_type: 'issue' | 'pr' | 'branch' | 'freeform'
    tracker: string
    is_child_loom: boolean
    one_shot_mode: 'default' | 'skip-reviews' | 'yolo'
    complexity_override: boolean
    create_only: boolean
    skip_bare_mode: boolean // Whether bare mode was skipped via setting
}

File: src/commands/start.ts (line 399)

Add skip_bare_mode property to the tracking call:

TelemetryService.getInstance().track('loom.created', {
    source_type: parsed.type === 'epic' ? 'issue' : parsed.type as LoomCreatedProperties['source_type'],
    tracker: this.issueTracker.providerName,
    is_child_loom: !!parentLoom,
    one_shot_mode: oneShotMap[input.options.oneShot ?? ''] ?? 'default',
    complexity_override: !!input.options.complexity,
    create_only: !!input.options.createOnly,
    skip_bare_mode: !!settings.skipBareMode,
})

Step 9: Documentation

File: docs/iloom-commands.md

Add a "Performance" section near the existing configuration documentation (after the VCS Provider Settings section, around line 2393):

**Performance Settings:**

| Setting | Type | Default | Description |
|---------|------|---------|-------------|
| `skipBareMode` | boolean | `false` | Skip bare mode for headless utility operations. Set to `true` for Enterprise accounts where bare mode always fails. Eliminates the try-then-fallback overhead on every `start`, `commit`, and `finish`. |

**Example:**

`.iloom/settings.json` or `~/.config/iloom-ai/settings.json`:
```json
{
  "skipBareMode": true
}

### Step 10: Tests

**File: `src/utils/__tests__/claude.test.ts`** (or create if needed)

Add tests for the `skipBareMode` behavior:

1. **Test: `launchClaude` skips auto-apply when `skipBareMode: true`** — Mock `resolveBareModeConfig` and verify it's NOT called when `skipBareMode: true` is passed with `headless: true` and `noSessionPersistence: true`.

2. **Test: `launchClaude` still auto-applies when `skipBareMode: false`** — Verify `resolveBareModeConfig` IS called when `skipBareMode` is false/undefined.

3. **Test: `skipBareMode` doesn't affect explicit `bare: true`** — When caller explicitly sets `bare: true`, `skipBareMode` should have no effect on the explicit bare block (line 179).

4. **Test: Settings schema accepts `skipBareMode`** — Parse settings with `skipBareMode: true` and verify it passes validation.

### Must-Haves (Phase 3)
- `LoomCreatedProperties` includes `skip_bare_mode: boolean`
- `start.ts` tracks `skip_bare_mode` in telemetry
- `docs/iloom-commands.md` documents `skipBareMode` setting with example
- Tests verify: skip behavior, non-skip default, no interference with explicit `bare: true`
- `pnpm build` succeeds
- `pnpm test` passes (at minimum for affected test files)

@acreeger

acreeger commented Jul 9, 2026

Copy link
Copy Markdown
Collaborator Author

Implementation Complete

Summary

Added skipBareMode configuration option that lets users bypass Neon bare-mode entirely, eliminating the try-then-fallback overhead on every start/commit/finish for accounts where bare mode always fails (e.g., Enterprise Accounts).

Changes Made

  • src/lib/SettingsManager.ts: Added skipBareMode to both settings schemas (default: false)
  • src/utils/claude.ts: Added skipBareMode to ClaudeCliOptions, guards auto-apply bare mode block
  • src/lib/CommitManager.ts: Thread skipBareMode from settings to launchClaude
  • src/lib/SessionSummaryService.ts: Thread skipBareMode through session summary and epic report paths
  • src/lib/IssueEnhancementService.ts: Thread skipBareMode to both launchClaude calls
  • src/lib/ValidationRunner.ts: Thread skipBareMode through all validation fix paths
  • src/lib/BranchNamingService.ts: Thread skipBareMode through Claude branch name strategy
  • src/commands/start.ts, commit.ts, finish.ts, cleanup.ts: Pass settings.skipBareMode to services
  • src/types/telemetry.ts: Track skip_bare_mode in loom.created event
  • docs/iloom-commands.md: Document skipBareMode setting with examples

Validation Results

  • ✅ Tests: 5242 passed / 5243 total (1 skipped)
  • ✅ Typecheck: Passed
  • ✅ Lint: Passed
  • ✅ Build: Passed

Detailed Changes by File (click to expand)

src/lib/SettingsManager.ts

Changes: Added setting definition

  • skipBareMode: z.boolean().default(false) in IloomSettingsSchema
  • skipBareMode: z.boolean().optional() in IloomSettingsSchemaNoDefaults

src/utils/claude.ts

Changes: Core skip logic

  • Added skipBareMode?: boolean to ClaudeCliOptions
  • Added && !skipBareMode guard to auto-apply bare mode condition
  • Added skipBareMode parameter to generateBranchName()

src/lib/CommitManager.ts

Changes: Caller propagation

  • Added skipBareMode param to generateClaudeCommitMessage()
  • Passed through to launchClaude() options

src/lib/SessionSummaryService.ts

Changes: Caller propagation

  • Threaded skipBareMode through launchSessionSummary() and epic report generation

src/lib/IssueEnhancementService.ts

Changes: Caller propagation

  • Added skipBareMode: settings.skipBareMode to both launchClaude() calls

src/lib/ValidationRunner.ts

Changes: Caller propagation

  • Threaded skipBareMode through runValidationsrunTypecheck/runLint/runTestsattemptClaudeFixlaunchClaude

src/lib/BranchNamingService.ts

Changes: Caller propagation

  • Added skipBareMode to ClaudeBranchNameStrategy and DefaultBranchNamingService

src/types/telemetry.ts

Changes: Telemetry tracking

  • Added skip_bare_mode: boolean to LoomCreatedProperties

src/utils/claude.test.ts

Changes: 3 new tests

  • skipBareMode prevents auto-apply
  • skipBareMode false still auto-applies
  • Explicit bare:true honored even with skipBareMode

docs/iloom-commands.md

Changes: Documentation

  • Added Database Branching Settings section with skipBareMode docs and examples

@acreeger
acreeger force-pushed the feat/issue-1028__skip-bare-mode branch from ca12269 to b8f7fc5 Compare July 9, 2026 22:47
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Status: No status

Development

Successfully merging this pull request may close these issues.

Add option to skip bare mode (avoid try-then-fallback overhead)

1 participant