Skip to content

Agent Branch Patterns

LumenFlow maintains a central registry of AI agent branch patterns that can bypass worktree requirements. This enables cloud-hosted agents and automation tools to work in the main checkout while still enforcing worktree discipline for human developers.

  1. Fetch from Registry - The isAgentBranch() function fetches patterns from lumenflow.dev/registry/agent-patterns.json
  2. Cache Locally - Patterns are cached for 7 days in ~/.lumenflow/cache/
  3. Merge with Config - Registry patterns are merged with any custom patterns from config
  4. Check Branch Name - Branch names are matched against glob patterns using micromatch

The registry includes patterns for popular AI agents:

{
  "patterns": [
    "agent/*",
    "claude/*",
    "codex/*",
    "copilot/*",
    "cursor/*",
    "aider/*",
    "cline/*",
    "continue/*",
    "devin/*",
    "windsurf/*",
    "bolt/*",
    "replit/*",
    "tabnine/*",
    "codeium/*",
    "ai/*",
    "bot/*",
    "automation/*"
  ]
}
import {
  isAgentBranch,
  isAgentBranchWithDetails,
} from '@lumenflow/packs-software-delivery/runtime/authoring';

// Check if a branch can bypass worktree requirements
if (await isAgentBranch('claude/session-12345')) {
  console.log('Agent branch detected - bypass allowed');
}

// Get detailed result for observability
const result = await isAgentBranchWithDetails('claude/session-123');
if (result.isMatch) {
  console.log(`Matched via ${result.patternResult.source}`);
  // source: 'registry' | 'merged' | 'override' | 'config' | 'defaults'
  console.log(`Registry fetched: ${result.patternResult.registryFetched}`);
}
#!/bin/bash
# Example pre-commit hook
if node_modules/.bin/is-agent-branch "$BRANCH"; then
  echo "Agent branch - skipping worktree check"
  exit 0
fi

For backwards compatibility, a sync version is available:

import { isAgentBranchSync } from '@lumenflow/packs-software-delivery/runtime/authoring';

// Uses only local config patterns, no registry fetch
const result = isAgentBranchSync('agent/task-123');

Configure agent patterns in workspace.yaml. There are three modes:

Custom patterns are merged with registry patterns:

# workspace.yaml
git:
  # These patterns are ADDED to registry patterns
  agentBranchPatterns:
    - 'my-custom-agent/*'
    - 'internal-tool/*'

Result: Your patterns + registry patterns (deduplicated)

| Option | Type | Default | Description | | ----------------------------- | -------- | ----------- | ----------------------------------- | | agentBranchPatterns | string[] | [] | Patterns to merge with registry | | agentBranchPatternsOverride | string[] | undefined | Patterns that replace registry | | disableAgentPatternRegistry | boolean | false | Skip network fetch (airgapped mode) |

| Registry Disabled | Override Set | Config Patterns | Result | Source | | ----------------- | ------------ | --------------- | ------------------------ | ---------- | | false | no | none | Registry patterns | registry | | false | no | ['custom/*'] | Config + Registry | merged | | false | yes | any | Override only | override | | true | no | none | Defaults (['agent/*']) | defaults | | true | no | ['custom/*'] | Config only | config | | true | yes | any | Override only | override |

Some branches are never bypassed, regardless of patterns:

  • Main branch (from git.mainBranch config)
  • master (legacy protected)
  • Lane branches (matching lane/*)
// These always return false
await isAgentBranch('main'); // false
await isAgentBranch('master'); // false
await isAgentBranch('lane/operations/wu-123'); // false

An agent-branch match changes where a client may work; it does not authorize one checkout to reuse another checkout’s mutable dependency graph.

For local worktree claims, LumenFlow runs a frozen package-manager install inside the new worktree. Workspace package links must resolve inside that worktree. A package manager may share its content-addressed store through hardlinks, reflinks, or copies materialized into checkout-local virtual storage; a direct workspace link may never resolve to an external store. Root or package-local node_modules directories are never linked to main or another worktree. Each intermediate dependency scope and package component is realpath-checked, including directory junctions. If local installation fails, claim setup fails without creating a shared fallback.

wu:claim --skip-setup creates no dependency links and runs no install. Before gates can run, set up dependencies inside that checkout. A missing/non-directory dependency root or missing declared workspace package fails preflight, and gates never substitutes main’s CLI dist for the worktree copy. Gate preflight reports the exact path and resolved target, then refuses to execute any gate command.

Older local worktrees can be converted through wu:rebase or wu:recover --id WU-XXX --action resume. Use wu:rebase --dry-run to report conversion-required without fetching, unlinking, installing, snapshotting or repairing main, or mutating Git. An isolated result also leaves main unsnapshotted. Actual conversion records deterministic link objects, continues through safe checkout-local ancestors, checks package-local dependency roots even when a package declares no workspace dependencies, and unlinks only the first broken or escaping object after revalidation. It never traverses or mutates the resolved target.

Once conversion starts, LumenFlow snapshots main before mutation and attests it afterward even if worktree unlink, install, or verification fails. An already-unsafe main is repaired to a safe graph without restoring that unsafe snapshot. If a safe pre-mutation snapshot later drifts, recovery also requires exact restoration of that safe baseline. That lifecycle attempt still hard-fails so the operator reruns from a clean, auditable boundary. Branch-only and branch-PR modes perform no local worktree conversion, even if a stale same-WU directory remains at the canonical path.

| Scenario | Behavior | | --------------------- | ----------------------------------------------- | | Fresh cache (<7d) | Use cached patterns, no network request | | Stale cache (>=7d) | Attempt fetch, use fresh if successful | | Fetch fails | Use stale cache if available, else use defaults | | No cache, no network | Use defaults (['agent/*']) |

Patterns are cached in:

~/.lumenflow/cache/agent-patterns-cache.json

Or if LUMENFLOW_HOME is set:

$LUMENFLOW_HOME/cache/agent-patterns-cache.json
import { clearCache } from '@lumenflow/packs-software-delivery/runtime/authoring';

// Clear in-memory cache (disk cache remains)
clearCache();

To clear disk cache, delete the cache file:

rm ~/.lumenflow/cache/agent-patterns-cache.json

Use resolveAgentPatterns() for testing or custom resolution:

import { resolveAgentPatterns } from '@lumenflow/packs-software-delivery/runtime/authoring';

// Resolve with custom options
const result = await resolveAgentPatterns({
  configPatterns: ['my-agent/*'],
  // overridePatterns: ['only-this/*'],  // Optional
  // disableAgentPatternRegistry: true,   // Optional
});

console.log(result.patterns); // ['my-agent/*', 'claude/*', ...]
console.log(result.source); // 'merged'
console.log(result.registryFetched); // true

For testing, inject a custom fetcher:

const mockFetcher = async () => ['test/*'];

const result = await resolveAgentPatterns({
  registryFetcher: mockFetcher,
});

expect(result.patterns).toEqual(['test/*']);

The registry is served as static JSON:

URL: https://lumenflow.dev/registry/agent-patterns.json

Response Schema:

interface RegistryResponse {
  version: string;
  description?: string;
  patterns: string[];
  documentation?: string;
  updated?: string;
}

To request a new agent pattern be added, include the agent name and the typical branch prefix in a community discussion (channels will be announced with v5.0.0). Patterns are added to the registry after review.

For CI/CD environments that don’t use agent branches, enable guarded headless mode:

export LUMENFLOW_HEADLESS=1
export CI=true  # or GITHUB_ACTIONS=true or LUMENFLOW_ADMIN=1

This bypasses all worktree checks. See AI Agent Integration for details.

The system is fail-closed:

  • Unknown branches are protected (require worktrees)
  • Detached HEAD is protected
  • Null/empty branches are protected
  • Network errors fall back to cache/defaults (not bypass)

This ensures accidental work in the wrong location is prevented.