import InfoBox from '../components/InfoBox.astro';
import PullQuote from '../components/PullQuote.astro';

When automated developer CLI synchronization was added in [developer-cli-auto-updates](/posts/developer-cli-auto-updates#non-mutating-startup-updates), a devcontainer startup hook was introduced to verify that the container's active Corepack package manager matched the `packageManager` specification in `package.json`. However, executing `pnpm` inside `sync-pnpm-version.sh` invoked a shimmed CLI wrapper that executed `sync-pnpm-version.sh` before running commands. This created an infinite recursive invocation loop during container initialization, stalling devcontainer creation before shell access opened.

## Identifying the recursive invocation path

Tracing the container startup hang revealed that Corepack's shimmed `pnpm` executable intercepted command calls and evaluated local synchronization scripts before passing arguments to Node.js.

```bash
#!/usr/bin/env bash
# .devcontainer/sync-pnpm-version.sh
if [ "${SYNCING_PNPM:-0}" = "1" ]; then
  exit 0
fi
export SYNCING_PNPM=1
```

Because the sync script called `pnpm --version` to check installed binaries, the binary shim re-executed `sync-pnpm-version.sh`, triggering an endless chain of nested shell processes.

<InfoBox variant="note" title="Environment recursion guard">
Setting an explicit `SYNCING_PNPM=1` environment variable during initial execution ensures nested sub-shells exit immediately rather than re-evaluating version checks.
</InfoBox>

## Direct JSON inspection over binary execution

To eliminate reliance on binary invocation during version checks, `sync-pnpm-version.sh` was updated to parse `package.json` directly using `jq` rather than invoking `pnpm` CLI commands.

<PullQuote attribution="Devcontainer initialization">
Reading configuration files directly prevents CLI wrapper shims from triggering nested lifecycle scripts during container boot.
</PullQuote>

Extracting version strings directly from file metadata allows the script to determine whether Corepack requires an update without launching child processes.

## Validating container boot stability

With environment recursion guards and direct JSON parsing in place, container startup executes deterministically without launching extra subprocesses.

```json
// .devcontainer/developer-clis.json
{
  "pnpm": {
    "packageManagerField": "packageManager",
    "syncScript": ".devcontainer/sync-pnpm-version.sh"
  }
}
```

Updating `.devcontainer/developer-clis.json` and verifying script execution in a fresh container environment confirmed that package manager synchronization completes cleanly without delaying devcontainer startup.