Skip to content

feat(adapters): toml_merge install strategy — codex T1 must merge into an existing .codex/config.toml #384

Description

@plind-junior

vouch install-mcp codex ships one file: adapters/codex/config.toml -> <project>/.codex/config.toml (project-local per the #179 scope decision). the installer's plain-copy path in src/vouch/install_adapter.py silently skips any destination that already exists:

if dst.exists():
    result.skipped.append(entry.dst)
    continue

.codex/config.toml is codex's primary config file — model, approval policy, and any other mcp servers all live there. so on any project where codex is already configured, the install is a silent no-op and vouch never gets wired. this is exactly the situation json_merge already solves for claude-code's .claude/settings.json; toml has no equivalent strategy.

proposed change

  • add a toml_merge: true entry flag in install.yaml manifests, next to fenced_append and json_merge (_FileEntry in src/vouch/install_adapter.py).
  • semantics mirror _install_json_merge: parse the existing destination, deep-merge the template's tables into it, write back. idempotent — re-running produces no change.
  • reading is stdlib (tomllib; we're requires-python >= 3.11). writing needs a decision: a minimal serializer that handles the shapes we ship (tables + string/array values only), or the small tomli-w dependency. either is fine — the hand-rolled serializer keeps the dependency set unchanged and our templates are deliberately simple.
  • flip adapters/codex/install.yaml T1 to toml_merge: true.

review gate & scope

installer-only; no kb read/write surface touched, nothing bypasses proposals.approve(). stays project-local — never touches ~/.codex/config.toml.

acceptance criteria

  • toml_merge: true supported in install.yaml entries and applied by install_adapter
  • installing over an existing .codex/config.toml preserves unrelated tables (e.g. [mcp_servers.other], top-level model settings) and adds [mcp_servers.vouch]
  • re-running the install is a no-op (reported as skipped/unchanged, file content stable)
  • fresh install (no existing file) still writes the full template
  • a conflicting existing [mcp_servers.vouch] is handled deterministically (documented which side wins, matching _install_json_merge's convention) and tested
  • tests in tests/test_install_adapter.py cover merge, idempotence, conflict, and fresh-install paths

Metadata

Metadata

Assignees

No one assigned

    Labels

    adaptersagent host adapters and install manifestsenhancementNew feature or requestsize: S50-199 changed non-doc lines

    Type

    No type

    Fields

    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions