EveryInc/compound-engineering-plugin · warning

Skipping ${storeDir}: resolves outside the managed root via

Error message

Skipping ${storeDir}: resolves outside the managed root via a symlinked ancestor (not modified)

What it means

storeRootEscapesManagedRoot in src/targets/managed-artifacts.ts guards against a symlinked ancestor directory making the managed store path resolve outside the writer's target root (e.g. ~/.codex). If the resolved store directory is not within rootDir, every cleanup and write that would act through that link is skipped, because they would otherwise modify whatever external location the user pointed the link at.

Source

Thrown at src/targets/managed-artifacts.ts:323

  const rel = path.relative(rootResolved, targetResolved)
  return rel === "" || (!rel.startsWith("..") && !path.isAbsolute(rel))
}

export async function isPathWithinRoot(rootDir: string, targetPath: string): Promise<boolean> {
  return isContainedPath(await realpathNearestExisting(rootDir), await realpathNearestExisting(targetPath))
}

/**
 * Guards a managed store directory against ancestor-symlink traversal. Returns
 * true (and warns once) when `storeDir` escapes `rootDir` via a symlinked
 * ancestor, meaning the caller must skip EVERY operation on that store --
 * cleanup sweeps and writes alike -- since all of them would otherwise act
 * through the link into whatever the user pointed it at. `rootDir` is the
 * writer's target root (e.g. `~/.codex`), not the store dir itself.
 */
export async function storeRootEscapesManagedRoot(rootDir: string, storeDir: string): Promise<boolean> {
  if (await isPathWithinRoot(rootDir, storeDir)) return false
  console.warn(`Skipping ${storeDir}: resolves outside the managed root via a symlinked ancestor (not modified)`)
  return true
}

View on GitHub (pinned to c9c10f8c75)

Solutions

  1. If the redirection is intentional, accept the warning: the converter intentionally will not follow the link; manage that location manually or via your dotfiles tooling.
  2. If the symlink is accidental, remove or replace it with a real directory (mkdir after rm) and re-run the install.
  3. If you want per-item links instead, install normally first, then replace individual managed items with symlinks — they will then be preserved per the user-managed-symlink rule.
  4. Verify with `readlink -f <storeDir>` that it resolves where you expect before re-running.

Example fix

// before
ls -la ~/.codex/skills  # symlink -> /data/shared/skills
// after
rm ~/.codex/skills
mkdir ~/.codex/skills
bun run convert --to codex
Defensive patterns

Strategy: validation

Validate before calling

import { realpath } from 'node:fs/promises'
import path from 'node:path'
async function storeWithinRoot(rootDir: string, storeDir: string): Promise<boolean> {
  const root = await realpath(rootDir).catch(() => rootDir)
  const store = await realpath(path.dirname(storeDir)).catch(() => path.dirname(storeDir))
  return store === root || store.startsWith(root + path.sep)
}
// if false, a symlinked ancestor redirects the store outside the root

Prevention

When it happens

Trigger: Calling any of promptsEscaped, skillsEscaped, agentsEscaped, commandsEscaped, pluginsEscaped, or extensionsEscaped when isPathWithinRoot(rootDir, storeDir) returns false — i.e. storeDir (or an ancestor) is a symlink resolving outside the target root such as ~/.codex.

Common situations: Users symlink ~/.codex/skills or ~/.codex/agents to a directory elsewhere (dotfiles sync, shared machines, secondary disks); users whose HOME is symlinked (e.g. /home/user -> /usr/home/user) so realpath resolution moves the store outside the nominal root.

Related errors


AI-assisted analysis of EveryInc/compound-engineering-plugin@c9c10f8c75 (2026-08-31). Data as JSON: /api/errors/de8c153ea13d73ee. Report an issue: GitHub.