Skip to content

fix(vm-agent): enable Codex MCP bootstrap on standalone/cf-container runtimes#1675

Draft
simple-agent-manager[bot] wants to merge 5 commits into
mainfrom
sam/execute-task-using-skill-cf48d7
Draft

fix(vm-agent): enable Codex MCP bootstrap on standalone/cf-container runtimes#1675
simple-agent-manager[bot] wants to merge 5 commits into
mainfrom
sam/execute-task-using-skill-cf48d7

Conversation

@simple-agent-manager

Copy link
Copy Markdown
Contributor

Summary

  • Problem: Dispatched Codex tasks on standalone/cf-container runtimes fail to call SAM MCP get_instructions because writeAgentStartupConfig returns early when startup.containerID == "", skipping Codex MCP config writing (~/.codex/config.toml) and SAM_MCP_TOKEN env var injection.
  • Fix: Move Codex config handling before the containerID == "" early return in writeAgentStartupConfig, and add a writeCodexConfigLocally function that writes config.toml directly to the local filesystem (vs. docker exec) for standalone/cf-container sessions. The local path reuses resolveLocalAuthFileTargetPath for CODEX_HOME support and mergeManagedCodexMcpConfig for safe config merging, mirroring the container path's behavior.
  • Scope: Only Codex gets the standalone path; OpenCode and Vibe remain gated by containerID (they may need separate fixes later). Container-based Codex is unchanged. Backward compatible.

Changed files

  • packages/vm-agent/internal/acp/session_host_startup.go — reorder writeAgentStartupConfig to call Codex before containerID early return; branch writeCodexStartupConfig on containerID
  • packages/vm-agent/internal/acp/gateway.go — add writeCodexConfigLocally with os.Chmod hardening, os.IsNotExist error discrimination
  • packages/vm-agent/internal/acp/gateway_test.go — 6 new tests (4 unit + 2 regression)
  • tasks/active/2026-07-25-fix-codex-mcp-bootstrap.md — task file

Validation

  • pnpm lint
  • pnpm typecheck
  • pnpm test
  • Additional validation run: go test ./internal/acp/... -count=1 — 16 successful, 0 failed
  • If this PR changes candidate selection for a sweep/cron/alarm loop: N/A — no sweep/cron changes

Staging Verification (REQUIRED for all code changes — merge-blocking)

DRAFT PR — staging verification deferred to merge time. Per .claude/rules/13-staging-verification.md, staging is a merge gate. This PR is intentionally kept as a draft and will not be merged until staging verification is completed:

  • Staging deployment green
  • Live app verified via Playwright
  • Existing workflows confirmed working
  • New feature/fix verified on staging — dispatch a Codex task on standalone/cf-container, confirm ~/.codex/config.toml written with SAM MCP entries, confirm SAM_MCP_TOKEN in process env, confirm get_instructions succeeds
  • Infrastructure verification completed — VM agent startup path changed; requires real node provisioning

Staging Verification Evidence

Draft PR — staging verification will be performed before merge.

UI Compliance Checklist (Required for UI changes)

N/A: Go-only vm-agent change, no UI surfaces touched.

End-to-End Verification (Required for multi-component changes)

  • Data flow traced from user input to final outcome with code path citations
  • Capability test exercises the complete happy path across system boundaries
  • All spec/doc assumptions about existing behavior verified against code — resolveLocalAuthFileTargetPath reuse verified; Codex CLI config path assumption needs live verification
  • If any gap exists: staging verification of real Codex session reading ~/.codex/config.toml is the remaining gap

Data Flow Trace

1. Task dispatched → workspace created on standalone/cf-container node
   → packages/vm-agent/internal/acp/session_host.go:startAgentWithSessionMode()

2. Agent startup prepared
   → session_host_startup.go:prepareAgentStartup() builds agentStartup{containerID: ""}

3. writeAgentStartupConfig called with agentType="openai-codex", containerID=""
   → session_host_startup.go:writeAgentStartupConfig()
   → NOW: Codex branch fires BEFORE containerID=="" early return (was AFTER — the bug)

4. writeCodexStartupConfig branches on containerID
   → session_host_startup.go:writeCodexStartupConfig()
   → containerID=="" → writeCodexConfigLocally() (NEW)
   → containerID!="" → writeCodexConfigToContainer() (existing)

5. writeCodexConfigLocally writes ~/.codex/config.toml + returns SAM_MCP_TOKEN env vars
   → gateway.go:writeCodexConfigLocally()
   → envVars appended to startup.envVars

6. Codex process started with SAM_MCP_TOKEN in environment
   → session_host_process.go:startLocalProcess() uses startup.envVars
   → Codex reads ~/.codex/config.toml, finds SAM MCP server entry
   → Codex calls get_instructions via SAM MCP

Untested Gaps

  • Real Codex CLI reading ~/.codex/config.toml from standalone runtime — verified via unit tests that the file is written correctly; live verification deferred to staging (draft PR).

Post-Mortem (Required for bug fix PRs)

What broke

Dispatched Codex tasks on standalone/cf-container runtimes could not call SAM MCP get_instructions — Codex started without MCP config or SAM_MCP_TOKEN, so it had no way to reach SAM's MCP server for project instructions, policies, or tools.

Root cause

writeAgentStartupConfig in session_host_startup.go returned early when startup.containerID == "" before reaching the Codex config writing branch. This early return was added for agents that only need container-based config (OpenCode, Vibe), but it also blocked Codex's config path for standalone/cf-container sessions where containerID is legitimately empty.

Class of bug

Early-return guard that over-broadly gates agent-specific setup — a guard intended for one set of agents (container-dependent agents) inadvertently blocked another agent (Codex) that needs to run its setup regardless of container presence.

Why it wasn't caught

  1. No test exercised writeAgentStartupConfig with agentType="openai-codex" and containerID="" — all existing Codex tests used container-based paths.
  2. The containerID == "" early return looked correct in isolation — it only became wrong when Codex's standalone/cf-container support was introduced.
  3. SAMEnvFallback (which provides workspace identity vars for standalone sessions) intentionally excludes SAM_MCP_TOKEN, so the missing token wasn't caught by env-var audits.

Process fix included in this PR

No new .claude/rules/ changes — the existing rules (02-quality-gates regression test requirements, 10-e2e-verification data flow tracing) would have caught this if applied. The regression tests added in this PR (TestWriteAgentStartupConfigCodexStandaloneWritesMcpConfig, TestWriteAgentStartupConfigNonCodexStandaloneIsNoop) are the primary process fix: they exercise the exact ordering bug at the function level and are proven discriminating (verified to fail on pre-fix code by the task-completion-validator).

Post-mortem file

tasks/active/2026-07-25-fix-codex-mcp-bootstrap.md

Specialist Review Evidence (Required for agent-authored PRs)

  • All local reviewers completed and findings addressed before merge
  • If any reviewer did NOT complete: N/A — all completed
Reviewer Status Outcome
test-engineer ADDRESSED 1 CRITICAL: no test at writeAgentStartupConfig level. Fixed in commit c34decf (2 regression tests added, proven discriminating)
go-specialist ADDRESSED 2 MEDIUM: missing os.Chmod after MkdirAll, missing os.IsNotExist check. Fixed in commit 5cc0b64. 1 HIGH: cross-session file race — pre-existing in container path too, not a regression
security-auditor ADDRESSED 0 CRITICAL/HIGH. MEDIUM-1 (file chmod): fixed in commit 0bed1ad. MEDIUM-2 (callback token in base_url): pre-existing pattern. MEDIUM-3 (read-merge-write race): pre-existing in container path
task-completion-validator PASS Code checks A/B/F pass. HIGH: staging verification needed — procedural merge gate, documented as prerequisite for future merge

Exceptions (If any)

  • Scope: Staging verification deferred (draft PR, not merging)
  • Rationale: User explicitly requested draft PR only, no merge. Staging verification is a merge gate per rules 13/22/30. Will be completed before any future merge.
  • Expiration: Must be completed before this PR leaves draft status.

Agent Preflight (Required)

  • Preflight completed before code changes

Classification

  • external-api-change
  • cross-component-change
  • business-logic-change
  • public-surface-change
  • docs-sync-change
  • security-sensitive-change
  • ui-change
  • infra-change

External References

N/A: No external APIs involved. Fix uses existing codebase patterns (resolveLocalAuthFileTargetPath, generateCodexMcpConfig, mergeManagedCodexMcpConfig).

Codebase Impact Analysis

  • packages/vm-agent/internal/acp/session_host_startup.go — writeAgentStartupConfig guard reordering, writeCodexStartupConfig branching
  • packages/vm-agent/internal/acp/gateway.go — new writeCodexConfigLocally function
  • No changes to apps/api, apps/web, packages/shared, or any other package

Documentation & Specs

N/A: Internal vm-agent change. No public-facing docs reference the standalone Codex MCP bootstrap path.

Constitution & Risk Check

  • Principle XI (No Hardcoded Values): config path derived from CODEX_HOME env var via resolveLocalAuthFileTargetPath; no hardcoded paths.
  • File permissions (0o700/0o600) enforced with explicit os.Chmod matching existing codebase patterns.
  • Backward compatible: container-based Codex path unchanged; other agents (OpenCode, Vibe) unchanged.

raphaeltm and others added 5 commits July 25, 2026 08:51
Codex agents on standalone/cf-container runtimes fail to load SAM MCP
tools because writeAgentStartupConfig returns early when containerID
is empty, skipping config.toml writing and SAM_MCP_TOKEN injection.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…runtimes

writeAgentStartupConfig returned early when containerID was empty,
skipping writeCodexStartupConfig for standalone and cf-container
sessions. This meant Codex never got SAM MCP config written to
~/.codex/config.toml and never received SAM_MCP_TOKEN env vars,
so dispatched Codex tasks couldn't call get_instructions.

Fix: move Codex handling before the containerID guard, and add
writeCodexConfigLocally that writes config.toml to the local
filesystem (mirroring writeCodexConfigToContainer). The local
path uses resolveLocalAuthFileTargetPath for CODEX_HOME support
and mergeManagedCodexMcpConfig for safe config merging.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Add two regression tests that exercise writeAgentStartupConfig directly:

- TestWriteAgentStartupConfigCodexStandaloneWritesMcpConfig: proves
  Codex with containerID="" gets config.toml written and SAM_MCP_TOKEN
  injected. This test would fail on the pre-fix code where the
  containerID=="" guard returned early before reaching the Codex branch.

- TestWriteAgentStartupConfigNonCodexStandaloneIsNoop: proves non-Codex
  agents (opencode, vibe, claude-code) are still correctly skipped when
  containerID is empty.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…issions

Address go-specialist review findings:
- Distinguish os.IsNotExist from real read errors (permission denied,
  I/O error) when reading existing config.toml — prevents silently
  discarding user config on transient read failures.
- Add explicit os.Chmod on .codex directory after MkdirAll, matching
  the sibling injectAuthFileCredential pattern — tightens permissions
  on pre-existing directories that may have looser umask defaults.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…fety

Add explicit os.Chmod(configPath, 0o600) after os.WriteFile, ensuring
tight permissions even when the file pre-exists with looser umask
defaults. Mirrors writeAuthFileToContainer's permission enforcement
pattern. Addresses security-auditor MEDIUM finding.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@sonarqubecloud

Copy link
Copy Markdown

@codspeed-hq

codspeed-hq Bot commented Jul 25, 2026

Copy link
Copy Markdown
Contributor

Merging this PR will not alter performance

✅ 6 untouched benchmarks


Comparing sam/execute-task-using-skill-cf48d7 (0bed1ad) with main (44adc7e)

Open in CodSpeed

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant