Skip to content

Add launch orchestrator and docker auth support - #54

Closed
clduab11 wants to merge 8 commits into
mainfrom
branch-4-devtest
Closed

Add launch orchestrator and docker auth support#54
clduab11 wants to merge 8 commits into
mainfrom
branch-4-devtest

Conversation

@clduab11

Copy link
Copy Markdown
Collaborator

Summary:

  • add shared doctor/launch engines, wiring them into the CLI along with env docker-login plumbing for MCP registries and docker auth remediations
  • document the new launch gate flow and docker-login instructions across docs, while keeping existing CLI conventions intact
  • cover the new behavior with dedicated doctor/launch tests plus service-manager/docker-login coverage and CLI smoke checks

Testing:

  • Not run (not requested)

@coderabbitai

coderabbitai Bot commented Feb 14, 2026

Copy link
Copy Markdown
Contributor
📝 Walkthrough

Summary by CodeRabbit

  • New Features

    • Added a gated launch workflow (JSON output, strict mode, profile selection) and env docker-login to authenticate registries.
  • Documentation

    • Updated guides and README to replace doctor-based readiness with the launch gate, add JSON-enabled examples, docker-login steps, and macOS startup gate guidance.
  • Tests

    • Added end-to-end and unit tests covering doctor, launch, env docker-login, and registry derivation workflows.

Walkthrough

Adds a JSON-enabled launch/startup gate and docker-login CLI commands, implements Doctor and Launch modules for multi-step readiness gating, extends ServiceManager with Docker metadata, introduces CliGateError and new error codes, updates docs to replace doctor-based flows with launch-based gating, and adds unit and e2e tests.

Changes

Cohort / File(s) Summary
CLI Core: launch & doctor modules
src/cli/doctor.ts, src/cli/launch.ts
New dependency-injected Doctor and Launch implementations, types (DoctorReport, LaunchReport), remediation collection, spawn normalization, and runDoctor/runLaunch orchestration with JSON output.
CLI Surface & wiring
src/cli/index.ts
Adds launch command (--json, --strict, --skip-codex-auth, --mcp-profiles) and env docker-login (--dry-run); wires defaults to DEFAULT_MCP_PROFILES and integrates doctor/launch flows. Duplicate command blocks noted in diff.
Service manager & profiles
src/env/service-manager.ts
Extends ServiceProfile with dockerImages/dockerRegistries; adds dockerImagesForProfiles, registriesForProfiles, dockerLogin and registry inference helper.
Errors
src/core/errors.ts
Adds ErrorCode values LAUNCH_GATE_FAILURE, DOCTOR_CHECKS_FAILED and new non-retryable CliGateError class.
Docs & guides
AGENTS.md, README.md, docs/guides/codex-macos-workflows.md, docs/guides/quick-start.md
Replaces doctor-based readiness with launch --json / launch --strict --json; documents docker-login, first-launch gate, remediation examples, and updated expected indicators.
Tests
tests/cli/doctor.test.ts, tests/cli/launch.test.ts, tests/env/service-manager.test.ts, tests/e2e/cli-smoke.test.ts
New unit and e2e tests covering doctor and launch flows, docker registry derivation, CLI help surfaces, and multiple failure/success scenarios.

Sequence Diagram(s)

sequenceDiagram
    participant User
    participant CLI as codex-synaptic CLI
    participant Daemon as Background Daemon
    participant MCP as MCP Services
    participant Codex as Codex Registry
    participant Doctor

    User->>CLI: codex-synaptic launch --json --strict
    CLI->>CLI: verify dist/cli/index.js exists & executable
    CLI->>Codex: (optional) check codex auth
    CLI->>Daemon: start background daemon
    Daemon-->>CLI: background started
    CLI->>MCP: ensure services up (ensureService / health-wait)
    MCP->>CLI: services healthy / status
    CLI->>Codex: register MCP profiles (codex mcp add)
    Codex-->>CLI: registration results
    CLI->>Doctor: run aggregated health checks
    Doctor-->>CLI: DoctorReport {ok, summary, checks}
    CLI-->>User: LaunchReport {ok: true|false, nextAction, remediations}
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~50 minutes

Possibly related PRs

Poem

🚪 A gate at boot, a JSON light,
🔐 Docker logins hum through night,
🩺 The Doctor counts each MCP heart,
🔁 Launch reports guide the next restart,
🌤️ "ok: true" — the startup song to continue.

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 21.43% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately summarizes the main changes: introducing a launch orchestrator and docker authentication support, which are the core features added in this PR.
Description check ✅ Passed The description is directly related to the changeset, covering the addition of doctor/launch engines, CLI wiring, env docker-login plumbing, documentation updates, and test coverage.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
  • 📝 Generate docstrings (stacked PR)
  • 📝 Generate docstrings (commit on current branch)
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch branch-4-devtest

Comment @coderabbitai help to get the list of available commands and usage tips.

@gemini-code-assist

This comment was marked as resolved.

gemini-code-assist[bot]

This comment was marked as outdated.

@coderabbitai coderabbitai Bot added codex OpenAI's Codex bot documentation Improvements or additions to documentation domain → infrastructure (Linear) enhancement New feature or request feature Feature of the repo general improvements General QOL improvements and random small bug fixex and patches labels Feb 14, 2026
coderabbitai[bot]

This comment was marked as outdated.

@cld-maindev cld-maindev deleted a comment from chatgpt-codex-connector Bot Feb 14, 2026
@coderabbitai coderabbitai Bot added p2-medium (Linear) Medium priority stack size-m labels Feb 14, 2026
@clduab11

Copy link
Copy Markdown
Collaborator Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Feb 14, 2026

Copy link
Copy Markdown
Contributor
✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Fix all issues with AI agents
In `@src/cli/launch.ts`:
- Around line 59-68: normalizeSpawn currently returns a synchronous wrapper that
may call spawnSync, which blocks the event loop during async runLaunch preflight
checks; change normalizeSpawn to be async and return an async spawn wrapper
(e.g., async (command, args, options) => { /* await deps.spawnCommand(...) or
run child process without blocking */ }) so it always returns a Promise of the
result, replace direct uses of spawnSync in normalizeSpawn with awaiting
deps.spawnCommand (or a non-blocking child process helper), and update all call
sites (the calls inside runLaunch that currently expect synchronous returns at
the checks for node --help, codex login status, and MCP registration) to await
the new async spawn function and handle its resolved
Pick<SpawnSyncReturns<string>, 'status'|'stdout'|'stderr'>-shaped result.
🧹 Nitpick comments (1)
src/cli/launch.ts (1)

28-33: Prefer an enum for nextAction to align with semantic-constant guidance.

Using a string union here drifts from the enum-only convention and makes reuse across modules harder. Consider introducing a small enum and using it in the report builder.

♻️ Suggested enum for launch next action
+export enum LaunchNextAction {
+  Continue = 'continue',
+  Stop = 'stop'
+}
+
 export interface LaunchReport {
   ok: boolean;
   steps: LaunchStep[];
   doctor: DoctorReport;
-  nextAction: 'continue' | 'stop';
+  nextAction: LaunchNextAction;
 }
...
-    nextAction: ok ? 'continue' : 'stop'
+    nextAction: ok ? LaunchNextAction.Continue : LaunchNextAction.Stop

As per coding guidelines: "Use enums for constants with semantic meaning".

Comment thread src/cli/launch.ts

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
src/cli/index.ts (1)

85-91: ⚠️ Potential issue | 🔴 Critical

Fix broken import block to resolve the TS1128 build failure.

The hive-mind helper identifiers are not preceded by an import { statement, leaving a dangling block that triggers the TS1128 error during build. Add the missing import declaration.

Proposed fix
 import { collectLaunchRemediations, runLaunch } from './launch.js';
-  executeGoapWorkflow,
-  executeTaskWithConsensus,
-  collectExecutionResults,
-  renderExecutionSummary,
-  setupWorkflowEventHandlers
-} from './hive-mind-helpers.js';
+import {
+  executeGoapWorkflow,
+  executeTaskWithConsensus,
+  collectExecutionResults,
+  renderExecutionSummary,
+  setupWorkflowEventHandlers
+} from './hive-mind-helpers.js';
🧹 Nitpick comments (2)
src/env/service-manager.ts (1)

331-339: Consider using ServiceManagerError instead of generic Error.

For consistency with the module's error handling pattern, the validation error should use the specialized error class defined at the top of this file.

♻️ Suggested improvement
 dockerLogin(registry: string): void {
   const normalized = registry.trim();
   if (!normalized) {
-    throw new Error('Docker registry is required for docker login.');
+    throw new ServiceManagerError('Docker registry is required for docker login.');
   }
   const cmd = `docker login ${normalized}`;
   this.logger.info('env', 'Authenticating Docker registry', { registry: normalized });
   execSync(cmd, { stdio: 'inherit' });
 }

As per coding guidelines: "Throw specific error types, not generic Error objects".

src/cli/launch.ts (1)

121-398: Consider extracting step handlers to reduce function complexity.

The runLaunch function spans ~280 lines with multiple distinct phases. While the structure is logical, extracting each step (preflight, auth, daemon, MCP up, codex register, doctor) into separate helper functions would improve readability and make individual steps easier to test in isolation.

This is flagged by static analysis as a complex method. A potential refactor pattern:

// Example extraction pattern
async function runPreflightStep(cwd: string, fileExists: Function, spawnCommand: Function): Promise<LaunchStep> {
  // ... preflight logic
}

async function runCodexAuthStep(options: LaunchOptions, spawnCommand: Function, cwd: string): Promise<LaunchStep> {
  // ... auth logic
}
// etc.

@clduab11

Copy link
Copy Markdown
Collaborator Author

@copilot , implement fix for below observation:

In @src/cli/launch.ts:

  • Around line 59-68: normalizeSpawn currently returns a synchronous wrapper that
    may call spawnSync, which blocks the event loop during async runLaunch preflight
    checks; change normalizeSpawn to be async and return an async spawn wrapper
    (e.g., async (command, args, options) => { /* await deps.spawnCommand(...) or
    run child process without blocking */ }) so it always returns a Promise of the
    result, replace direct uses of spawnSync in normalizeSpawn with awaiting
    deps.spawnCommand (or a non-blocking child process helper), and update all call
    sites (the calls inside runLaunch that currently expect synchronous returns at
    the checks for node --help, codex login status, and MCP registration) to await
    the new async spawn function and handle its resolved
    Pick<SpawnSyncReturns, 'status'|'stdout'|'stderr'>-shaped result.

Copilot AI commented Feb 14, 2026

Copy link
Copy Markdown
Contributor

@clduab11 I've opened a new pull request, #55, to work on those changes. Once the pull request is ready, I'll request review from you.

Copilot AI and others added 4 commits February 14, 2026 13:21
- Update DoctorDependencies.spawnCommand to return Promise
- Update LaunchDependencies (extends DoctorDependencies)
- Modify normalizeSpawn to return async wrapper function
- Await all spawnCommand calls in launch.ts (preflight, auth, mcp registration)
- Await all spawnCommand calls in doctor.ts (cli help, login status, mcp list)
- Update launch.test.ts with async spawnCommand mocks
- Update doctor.test.ts with async spawnCommand mocks
- All tests pass (9/9)

Co-authored-by: clduab11 <185000089+clduab11@users.noreply.github.com>
Replace wrapped spawnSync with proper async spawn implementation:
- Add spawnAsync helper using child_process.spawn with Promise
- Update doctor.ts to use spawnAsync by default
- Update launch.ts to use spawnAsync by default
- Prevents actual event loop blocking (not just wrapping sync in async)
- All tests still pass (9/9)

Co-authored-by: clduab11 <185000089+clduab11@users.noreply.github.com>
Document that spawnAsync always resolves (never rejects) to match spawnSync API:
- Errors communicated via status code and stderr
- Matches expected behavior of calling code
- Prevents unnecessary try/catch blocks in callers
- All tests still pass (9/9)

Co-authored-by: clduab11 <185000089+clduab11@users.noreply.github.com>
Make normalizeSpawn truly async to prevent event loop blocking

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 5

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/cli/doctor.ts`:
- Line 1: Remove the unused spawnSync import and the associated type
SpawnSyncReturns from the top-level child_process import; update the import line
that currently imports "spawn, spawnSync, type SpawnSyncReturns" to only import
"spawn" (and any other actually used symbols) so there are no unused imports
left (look for the import statement that mentions spawnSync and SpawnSyncReturns
in this module).
- Around line 94-130: The parseCodexMcpNames function currently throws a generic
Error for unsupported payloads; replace that with the project's specific error
type by importing BridgeError and ErrorCode and throw new
BridgeError(ErrorCode.MCP_ERROR, 'Unsupported JSON format returned by `codex mcp
list --json`.') (or, if preferred, create and throw a new Codex-specific error
class that extends CodexSynapticError) so MCP parsing failures are classified
consistently with the error handling used in src/cli/launch.ts.
- Around line 33-38: In spawnAsync (the child process handling code with
child.on('close')), change the close handler to accept both (code, signal), set
the resolved status to code ?? 1 (so signal-terminated children are treated as
failures), and include the signal value in the resolved object (e.g., add a
signal property alongside stdout and stderr) to provide better error context;
update any callers or types if necessary to account for the new signal field.

In `@src/cli/launch.ts`:
- Line 1: The import list in the module still includes the unused symbol
spawnSync from 'child_process' even though only the async spawn wrapper is used;
remove spawnSync from the import statement so it only imports spawn (and type
SpawnSyncReturns if still used) or drop the unused type import entirely to
eliminate the dead import.
- Around line 47-53: The close handler treats signal-terminated processes as
success by using code ?? 0; change the listener signature to (code, signal) and
set the returned status to a non-zero value when code is null (e.g., status =
code !== null ? code : 1) and include the signal name (signal) in the stderr or
add it to the resolved object so callers can see the termination reason; update
the child.on('close', ...) block that currently resolves { status: code ?? 0,
stdout, stderr } accordingly.

ℹ️ Review info

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between d98a7c7 and d68c493.

📒 Files selected for processing (4)
  • src/cli/doctor.ts
  • src/cli/launch.ts
  • tests/cli/doctor.test.ts
  • tests/cli/launch.test.ts

Comment thread src/cli/doctor.ts
@@ -0,0 +1,304 @@
import { spawn, spawnSync, type SpawnSyncReturns } from 'child_process';

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Drop the unused spawnSync import.
It’s never referenced in this file.

🧹 Suggested fix
-import { spawn, spawnSync, type SpawnSyncReturns } from 'child_process';
+import { spawn, type SpawnSyncReturns } from 'child_process';
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
import { spawn, spawnSync, type SpawnSyncReturns } from 'child_process';
import { spawn, type SpawnSyncReturns } from 'child_process';
🧰 Tools
🪛 GitHub Check: CodeFactor

[notice] 1-1: src/cli/doctor.ts#L1
'spawnSync' is defined but never used. (@typescript-eslint/no-unused-vars)

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/cli/doctor.ts` at line 1, Remove the unused spawnSync import and the
associated type SpawnSyncReturns from the top-level child_process import; update
the import line that currently imports "spawn, spawnSync, type SpawnSyncReturns"
to only import "spawn" (and any other actually used symbols) so there are no
unused imports left (look for the import statement that mentions spawnSync and
SpawnSyncReturns in this module).

Comment thread src/cli/doctor.ts
Comment on lines +33 to +38
child.on('close', (code) => {
resolve({
status: code ?? 0,
stdout,
stderr
});

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

# First, check the actual code in the file
cat -n src/cli/doctor.ts | head -50

Repository: clduab11/codex-synaptic

Length of output: 1753


🌐 Web query:

Node.js child_process close event code null signal termination behavior

💡 Result:

In Node.js child_process, the 'close' event callback receives (code, signal):

  • code is the numeric exit code only if the child exited normally; otherwise it’s null.
  • signal is the signal name (e.g., 'SIGTERM', 'SIGKILL') only if the child was terminated by a signal; otherwise it’s null.
  • Exactly one of code or signal will be non-null. If you see code === null, interpret that as “the process did not exit normally; it was ended by a signal” and look at signal to know which one. [1]

'close' vs 'exit':

  • 'exit' fires when the process ends, but its stdio streams may still be open.
  • 'close' fires after the process ends and all stdio streams for that child are closed, and it will always happen after 'exit' (or after 'error' if the child failed to spawn). [1]

Source: Node.js child_process docs ('close' / 'exit' events). [1]

References
[1] Node.js v22.19.0 Documentation – child_process (ChildProcess events 'close' and 'exit'). (nodejs.org)

Citations:


🏁 Script executed:

# Check if spawnSync is used elsewhere in the file
rg "spawnSync" src/cli/doctor.ts -A 2 -B 2

Repository: clduab11/codex-synaptic

Length of output: 466


🏁 Script executed:

# Check the entire file for any actual use of spawnSync (not just import/comment)
wc -l src/cli/doctor.ts && echo "---" && grep -n "spawnSync" src/cli/doctor.ts

Repository: clduab11/codex-synaptic

Length of output: 253


Treat signal-terminated child processes as failures in spawnAsync.

When a child process is killed by signal, Node.js sets code to null and populates signal instead. The current code ?? 0 fallback incorrectly reports success for these signal terminations. Update the fallback to code ?? 1 and capture the signal parameter for better error context.

🔧 Suggested fix
-    child.on('close', (code) => {
+    child.on('close', (code, signal) => {
       resolve({
-        status: code ?? 0,
+        status: code ?? 1,
         stdout,
         stderr
       });
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/cli/doctor.ts` around lines 33 - 38, In spawnAsync (the child process
handling code with child.on('close')), change the close handler to accept both
(code, signal), set the resolved status to code ?? 1 (so signal-terminated
children are treated as failures), and include the signal value in the resolved
object (e.g., add a signal property alongside stdout and stderr) to provide
better error context; update any callers or types if necessary to account for
the new signal field.

Comment thread src/cli/doctor.ts
Comment on lines +94 to +130
function parseCodexMcpNames(payload: unknown): string[] {
if (Array.isArray(payload)) {
return payload
.map((entry) => {
if (!entry || typeof entry !== 'object') {
return undefined;
}
return String((entry as { name?: string }).name ?? '');
})
.filter(Boolean) as string[];
}

if (payload && typeof payload === 'object') {
const candidateArrays = [
(payload as { servers?: unknown }).servers,
(payload as { items?: unknown }).items,
(payload as { mcpServers?: unknown }).mcpServers
];

for (const candidate of candidateArrays) {
if (!Array.isArray(candidate)) {
continue;
}

return candidate
.map((entry) => {
if (!entry || typeof entry !== 'object') {
return undefined;
}
return String((entry as { name?: string }).name ?? '');
})
.filter(Boolean) as string[];
}
}

throw new Error('Unsupported JSON format returned by `codex mcp list --json`.');
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

🧩 Analysis chain

🏁 Script executed:

# Check if file exists and read the relevant section
if [ -f src/cli/doctor.ts ]; then
  echo "=== File exists, reading lines 90-135 ==="
  sed -n '90,135p' src/cli/doctor.ts | cat -n
else
  echo "File not found"
fi

Repository: clduab11/codex-synaptic

Length of output: 1838


🏁 Script executed:

# Search for existing error classes in the codebase to understand patterns
rg "class.*Error.*extends" src/ -A 3 | head -50

Repository: clduab11/codex-synaptic

Length of output: 2913


🏁 Script executed:

# Check if CodexMcpParseError is already defined anywhere
rg "CodexMcpParseError" src/

Repository: clduab11/codex-synaptic

Length of output: 49


🏁 Script executed:

# Search for throw statements in the doctor.ts file
rg "throw" src/cli/doctor.ts -B 2 -A 1

Repository: clduab11/codex-synaptic

Length of output: 156


🏁 Script executed:

# Check what errors are imported or used in doctor.ts
head -40 src/cli/doctor.ts | cat -n

Repository: clduab11/codex-synaptic

Length of output: 1530


🏁 Script executed:

# Check the CodexSynapticError structure and ErrorCode enum
cat -n src/core/errors.ts | head -60

Repository: clduab11/codex-synaptic

Length of output: 2089


🏁 Script executed:

# Check how other CLI files handle errors
rg "throw" src/cli/ -B 3 -A 1 | head -60

Repository: clduab11/codex-synaptic

Length of output: 3274


Use a specific error type for unsupported MCP list payloads.
Line 129 throws a generic Error, which violates the guideline to use specific error types. This matters for consistent error classification and handling.

Consider using the existing error architecture: import BridgeError from ../core/errors.js and throw new BridgeError(ErrorCode.MCP_ERROR, 'Unsupported JSON format returned by codex mcp list --json.') to align with how src/cli/launch.ts handles MCP errors. Alternatively, create a dedicated error class extending CodexSynapticError if MCP parsing errors warrant their own type.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/cli/doctor.ts` around lines 94 - 130, The parseCodexMcpNames function
currently throws a generic Error for unsupported payloads; replace that with the
project's specific error type by importing BridgeError and ErrorCode and throw
new BridgeError(ErrorCode.MCP_ERROR, 'Unsupported JSON format returned by `codex
mcp list --json`.') (or, if preferred, create and throw a new Codex-specific
error class that extends CodexSynapticError) so MCP parsing failures are
classified consistently with the error handling used in src/cli/launch.ts.

Comment thread src/cli/launch.ts
@@ -0,0 +1,443 @@
import { spawn, spawnSync, type SpawnSyncReturns } from 'child_process';

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Remove unused spawnSync import.
It isn’t referenced after the async spawn wrapper was added.

🧹 Suggested fix
-import { spawn, spawnSync, type SpawnSyncReturns } from 'child_process';
+import { spawn, type SpawnSyncReturns } from 'child_process';
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
import { spawn, spawnSync, type SpawnSyncReturns } from 'child_process';
import { spawn, type SpawnSyncReturns } from 'child_process';
🧰 Tools
🪛 GitHub Check: CodeFactor

[notice] 1-1: src/cli/launch.ts#L1
'spawnSync' is defined but never used. (@typescript-eslint/no-unused-vars)

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/cli/launch.ts` at line 1, The import list in the module still includes
the unused symbol spawnSync from 'child_process' even though only the async
spawn wrapper is used; remove spawnSync from the import statement so it only
imports spawn (and type SpawnSyncReturns if still used) or drop the unused type
import entirely to eliminate the dead import.

Comment thread src/cli/launch.ts
Comment on lines +47 to +53
child.on('close', (code) => {
resolve({
status: code ?? 0,
stdout,
stderr
});
});

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

# First, examine the actual code in src/cli/launch.ts around the flagged lines
head -100 src/cli/launch.ts | cat -n

Repository: clduab11/codex-synaptic

Length of output: 3309


🌐 Web query:

Node.js child_process close event code null signal terminated behavior

💡 Result:

In Node.js, the 'close' event callback is ('close', (code, signal) => { ... }), where:

  • code is the numeric exit code only if the child exited normally; it is null if the child was terminated by a signal. [1]
  • signal is the signal name (e.g., 'SIGTERM', 'SIGKILL') only if the child was terminated by a signal; otherwise it is null. [1]
  • Exactly one of code or signal will be non-null. [1]
  • 'close' fires after the process ends and after its stdio streams are closed, and it always happens after the child’s 'exit' event (or after 'error' if the child failed to spawn). [1]

So if you see code === null and signal === 'SIGTERM' (or similar), that’s the expected “terminated by signal” behavior—not an error by itself. [1]

Sources:
[1] Node.js documentation — ChildProcess 'close' event semantics (exit code null when terminated by signal; ordering vs 'exit'). (nodejs.org)

Citations:


Treat signal-terminated processes as failures, not success.

Line 49 uses code ?? 0, which treats processes killed by signals (SIGTERM, SIGKILL, etc.) as having status 0 (success). In Node.js, when a child is terminated by a signal, the close event callback receives code === null with the signal name in the second parameter. The current code incorrectly handles this as success.

🔧 Suggested fix
-    child.on('close', (code) => {
+    child.on('close', (code, signal) => {
       resolve({
-        status: code ?? 0,
+        status: signal ? 1 : (code ?? 1),
         stdout,
         stderr
       });
     });
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
child.on('close', (code) => {
resolve({
status: code ?? 0,
stdout,
stderr
});
});
child.on('close', (code, signal) => {
resolve({
status: signal ? 1 : (code ?? 1),
stdout,
stderr
});
});
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/cli/launch.ts` around lines 47 - 53, The close handler treats
signal-terminated processes as success by using code ?? 0; change the listener
signature to (code, signal) and set the returned status to a non-zero value when
code is null (e.g., status = code !== null ? code : 1) and include the signal
name (signal) in the stderr or add it to the resolved object so callers can see
the termination reason; update the child.on('close', ...) block that currently
resolves { status: code ?? 0, stdout, stderr } accordingly.

@clduab11 clduab11 removed the size-m label Feb 24, 2026
@clduab11 clduab11 closed this Feb 24, 2026
@clduab11

Copy link
Copy Markdown
Collaborator Author

Closed; outdated.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

codex OpenAI's Codex bot documentation Improvements or additions to documentation domain → infrastructure (Linear) enhancement New feature or request feature Feature of the repo general improvements General QOL improvements and random small bug fixex and patches p2-medium (Linear) Medium priority stack

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants