What is the impact?
A hook or command interrupts normal work. In the affected setup, opening or resuming an OMP session can create a recursive process chain and make the host unresponsive. The failure is not limited to a warning: every recursive OMP instance can initialize ECC and configured MCP integrations again.
What happened?
When ECC's Pi adapter runs the session_start hook under compiled Oh My Pi (OMP), the adapter launches the hook with process.execPath. In OMP, that executable is the omp binary rather than Node, so the effective command becomes:
omp .../ecc-universal/scripts/hooks/run-with-flags.js session:start ...
The child OMP loads ECC again and repeats the same hook:
omp -> ECC session_start -> omp -> ECC session_start -> ...
This causes repeated OMP/MCP child processes, high CPU and swap pressure, and eventually:
handler timed out after 30000ms
ECC session-start hook skipped
A live reproduction on a 20-CPU Linux host produced approximately 78 omp processes, 290 node-MainThread processes, and repeated codebase-memory, node_repl, and chrome-devtools descendants. The load average exceeded 40. After terminating only processes whose command line matched the recursive run-with-flags.js session:start invocation, the matching process count went to zero and the load average fell to approximately 2.
Harness
Oh My Pi (OMP), compiled binary.
The issue is especially visible with omp --resume <session-id> because resume dispatches the Pi session_start lifecycle event with reason resume. A new OMP session can trigger the same path.
Install method
Other: ECC universal plugin loaded by OMP from its plugin directory.
Operating system
Linux (x86_64), Arch-based distribution.
ECC and harness versions
Reproduction details
The current upstream adapter uses the following shape in .pi/extensions/index.ts:
const child = execFile(
process.execPath,
[HOOK_RUNNER, spec.id, spec.script, spec.profiles],
options,
callback,
)
The compiled OMP runtime reports values equivalent to:
process.release.name = node
process.versions.bun = 1.4.0
process.execPath = .../omp
This is important because process.release.name === "node" is not sufficient to identify the executable in a compiled OMP runtime.
The minimal hook invocation has different behavior depending on the executable:
node run-with-flags.js session:start scripts/hooks/session-start.js minimal,standard,strict
# completes successfully
omp run-with-flags.js session:start scripts/hooks/session-start.js minimal,standard,strict
# starts another OMP instance, which loads ECC and repeats the hook
A redacted equivalent diagnostic is:
console.log({
releaseName: process.release?.name,
bunVersion: process.versions?.bun,
execPath: process.execPath,
})
Expected behavior
The adapter should run each ECC hook once with a Node runtime. Compiled OMP/Bun should use node or an explicit configured Node executable, while a normal Node-hosted Pi process may use process.execPath.
Starting or resuming a session must not create another OMP process for the hook itself.
Root cause assessment
This is a compatibility defect in the adapter, exposed by a valid compiled OMP/Bun runtime. It is not a project-specific configuration error:
- The installed Node executable can run the hook successfully.
- The hook path and working directory are valid.
- The adapter assumes that the host process executable is also the script runtime.
- The upstream Pi adapter was tested with Node/Pi on macOS, but the merged PR does not cover compiled OMP/Bun.
The 30-second hook timeout is a secondary issue: it is equal to OMP's 30-second lifecycle handler limit, so the recursive failure has no timeout headroom. It should be considered separately from the runtime-selection fix.
Suggested fix
Resolve the hook runtime separately from the host executable, for example:
const isNodeRuntime =
process.release?.name === "node" &&
!process.versions?.bun &&
/^(?:node|nodejs)(?:\.exe)?$/i.test(path.basename(process.execPath))
const HOOK_RUNTIME =
process.env.ECC_HOOK_NODE?.trim() ||
(isNodeRuntime ? process.execPath : "node")
The exact implementation can differ, but it should:
- Never use compiled OMP's
process.execPath as the hook interpreter.
- Preserve
execFile with a separate argv array and no shell.
- Support an absolute
ECC_HOOK_NODE override for installations where Node is not on PATH.
- Add behavioral coverage for normal Node, compiled OMP/Bun, and the explicit override.
Validation
A local installed-package workaround using this runtime guard was validated with:
- 27 adapter tests passing.
- A fresh OMP startup smoke test with zero recursive
run-with-flags.js session:start processes.
- The direct hook completing under Node.
- The original recursive process pattern reproduced with compiled OMP before the guard was applied.
The local workaround is outside the ECC repository and can be overwritten by a plugin reinstall; this Issue is intended to track the durable upstream fix.
Scope and compatibility
The proposed change does not need to alter Pi lifecycle registration, ECC hook stdin/stdout contracts, project working-directory resolution, profile gating, context injection, or shell-safety behavior. It only selects the correct executable for the existing hook invocation.
Related records
- #2759 introduced the Pi adapter and currently uses
process.execPath.
- #2352 is the superseded broad Pi integration.
- #2687 and #2688 concern inline
node -e bootstraps and Windows Defender, not compiled OMP runtime selection.
- The current upstream source is visible at
.pi/extensions/index.ts.
What is the impact?
A hook or command interrupts normal work. In the affected setup, opening or resuming an OMP session can create a recursive process chain and make the host unresponsive. The failure is not limited to a warning: every recursive OMP instance can initialize ECC and configured MCP integrations again.
What happened?
When ECC's Pi adapter runs the
session_starthook under compiled Oh My Pi (OMP), the adapter launches the hook withprocess.execPath. In OMP, that executable is theompbinary rather than Node, so the effective command becomes:The child OMP loads ECC again and repeats the same hook:
This causes repeated OMP/MCP child processes, high CPU and swap pressure, and eventually:
A live reproduction on a 20-CPU Linux host produced approximately 78
ompprocesses, 290node-MainThreadprocesses, and repeatedcodebase-memory,node_repl, andchrome-devtoolsdescendants. The load average exceeded 40. After terminating only processes whose command line matched the recursiverun-with-flags.js session:startinvocation, the matching process count went to zero and the load average fell to approximately 2.Harness
Oh My Pi (OMP), compiled binary.
The issue is especially visible with
omp --resume <session-id>because resume dispatches the Pisession_startlifecycle event with reasonresume. A new OMP session can trigger the same path.Install method
Other: ECC universal plugin loaded by OMP from its plugin directory.
Operating system
Linux (x86_64), Arch-based distribution.
ECC and harness versions
Reproduction details
The current upstream adapter uses the following shape in
.pi/extensions/index.ts:The compiled OMP runtime reports values equivalent to:
This is important because
process.release.name === "node"is not sufficient to identify the executable in a compiled OMP runtime.The minimal hook invocation has different behavior depending on the executable:
A redacted equivalent diagnostic is:
Expected behavior
The adapter should run each ECC hook once with a Node runtime. Compiled OMP/Bun should use
nodeor an explicit configured Node executable, while a normal Node-hosted Pi process may useprocess.execPath.Starting or resuming a session must not create another OMP process for the hook itself.
Root cause assessment
This is a compatibility defect in the adapter, exposed by a valid compiled OMP/Bun runtime. It is not a project-specific configuration error:
The 30-second hook timeout is a secondary issue: it is equal to OMP's 30-second lifecycle handler limit, so the recursive failure has no timeout headroom. It should be considered separately from the runtime-selection fix.
Suggested fix
Resolve the hook runtime separately from the host executable, for example:
The exact implementation can differ, but it should:
process.execPathas the hook interpreter.execFilewith a separate argv array and no shell.ECC_HOOK_NODEoverride for installations where Node is not onPATH.Validation
A local installed-package workaround using this runtime guard was validated with:
run-with-flags.js session:startprocesses.The local workaround is outside the ECC repository and can be overwritten by a plugin reinstall; this Issue is intended to track the durable upstream fix.
Scope and compatibility
The proposed change does not need to alter Pi lifecycle registration, ECC hook stdin/stdout contracts, project working-directory resolution, profile gating, context injection, or shell-safety behavior. It only selects the correct executable for the existing hook invocation.
Related records
process.execPath.node -ebootstraps and Windows Defender, not compiled OMP runtime selection..pi/extensions/index.ts.