Skip to content

feat(adapters): toml_merge install strategy for codex config.toml#392

Merged
plind-junior merged 2 commits into
vouchdev:testfrom
dripsmvcp:feat/codex-toml-merge
Jul 6, 2026
Merged

feat(adapters): toml_merge install strategy for codex config.toml#392
plind-junior merged 2 commits into
vouchdev:testfrom
dripsmvcp:feat/codex-toml-merge

Conversation

@dripsmvcp

Copy link
Copy Markdown
Contributor

.codex/config.toml is codex's primary config file — model, approval policy, and any other mcp servers all live there. the installer's plain-copy path silently skips an existing destination, so on any project where codex was already configured, vouch install-mcp codex was a silent no-op and vouch never got wired. this is the toml equivalent of the situation json_merge already solves for claude-code's .claude/settings.json.

this adds a toml_merge: true entry flag next to fenced_append and json_merge, with semantics mirroring _install_json_merge: parse the existing destination with stdlib tomllib, deep-merge the template's tables into it, write back through the merged result channel. conflicts are deterministic and match the json_merge convention — the user's existing value always wins; only genuinely missing keys (including nested ones, e.g. a missing env table under an existing [mcp_servers.vouch]) are filled in. re-running is a flat no-op reported as skipped. the codex T1 manifest entry is flipped to toml_merge: true.

on the writer decision flagged in the ticket: hand-rolled serializer, not tomli-w, keeping the dependency set unchanged. it covers the shapes tomllib can hand us from real configs (tables, arrays, inline tables inside arrays, quoted keys, scalars, datetimes) and self-checks: the serialized output must round-trip through tomllib.loads back to exactly the merged data, and anything it can't faithfully re-emit degrades to skipped rather than risking the user's config. malformed existing files are likewise left untouched, matching json_merge. comments and formatting in a merged file are not preserved — the same trade-off _install_json_merge already makes.

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

tests cover: merge preserving unrelated tables and top-level values, idempotent re-run with stable content, fresh install writing the full template, existing-vouch-entry conflict (user wins, missing env table still added), malformed toml skipped untouched, serializer round-trip of the shipped shapes, and no-change detection. verified end-to-end via vouch install-mcp codex --tier T1 against a pre-seeded config: unrelated [mcp_servers.other] and model survive, [mcp_servers.vouch] lands next to them, and the re-run reports already present.

closes #384

@coderabbitai

coderabbitai Bot commented Jul 6, 2026

Copy link
Copy Markdown

Important

Review skipped

Auto reviews are disabled on base/target branches other than the default branch.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: d6cac5db-519e-47e1-8290-d3b95fb96aab

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

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

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Pull request overview

Adds a TOML-merge install strategy so vouch install-mcp codex can wire vouch into an existing .codex/config.toml (Codex’s primary project config) instead of silently skipping when the file already exists. This mirrors the existing json_merge behavior used for .claude/settings.json, keeping installs idempotent and preserving user-owned values.

Changes:

  • Introduces toml_merge: true manifest flag and installer path that deep-merges missing TOML keys using tomllib, with “user wins” conflict semantics.
  • Implements a minimal TOML serializer with round-trip validation to avoid clobbering user config when serialization can’t be done safely.
  • Updates the Codex T1 manifest to use toml_merge and adds tests covering merge behavior, conflicts, idempotence, malformed TOML, and serializer round-trips.

Reviewed changes

Copilot reviewed 3 out of 3 changed files in this pull request and generated 1 comment.

File Description
src/vouch/install_adapter.py Adds toml_merge manifest support, TOML deep-merge logic, and TOML serialization + safe rewrite behavior.
tests/test_install_adapter.py Adds focused tests for Codex TOML merging, idempotence, conflict handling, malformed TOML skip, and serializer round-trip.
adapters/codex/install.yaml Switches Codex T1 install from copy/skip to toml_merge: true with updated rationale comments.

Comment thread src/vouch/install_adapter.py Outdated
Comment on lines +145 to +147
fenced = bool(raw.get("fenced_append", False))
json_merge = bool(raw.get("json_merge", False))
toml_merge = bool(raw.get("toml_merge", False))
dripsmvcp added 2 commits July 6, 2026 17:32
vouchdev#361 inserted `_log = logging.getLogger(...)` between two import
groups in jsonl_server.py. that statement-among-imports trips ruff's
E402 on every import that follows it, so `ruff check src tests` — the
lint gate in ci — now fails on the whole repo. move the logger
definition below the imports (where storage.py already keeps its own
module logger); no behaviour change.
.codex/config.toml is codex's primary config file, so the plain-copy
path silently skipped any project where codex was already configured
and vouch never got wired. add a toml_merge entry flag mirroring
json_merge: parse the existing destination with tomllib, deep-merge
the template's tables into it (existing user values always win on
conflict), and write back. flip the codex T1 entry to toml_merge.

writing uses a minimal hand-rolled serializer (tables, arrays, inline
tables in arrays, scalars, datetimes) so the dependency set stays
unchanged; the output must survive a tomllib round-trip back to the
merged data, and anything the serializer can't faithfully re-emit
degrades to skipped rather than risking the user's config.

closes vouchdev#384
@dripsmvcp dripsmvcp force-pushed the feat/codex-toml-merge branch from 9c6fd31 to 1a3f1c3 Compare July 6, 2026 08:36
@github-actions github-actions Bot added the mcp mcp, jsonl, and http surfaces label Jul 6, 2026
@dripsmvcp

Copy link
Copy Markdown
Contributor Author

addressed the copilot review: manifest flag parsing now requires actual yaml booleans — a mistakenly-quoted toml_merge: "false" no longer coerces to true — and rejects an entry that sets more than one merge strategy, both raising a clear AdapterError. tests added for the non-boolean, multiple-strategy, and valid-boolean cases.

also rebased this branch onto a small jsonl_server.py lint fix (#399): #361 left ruff check src tests red repo-wide (an E402 from a logger defined mid-import-block), which was failing ci on every open pr. this branch carries that fix so its lint passes; it's a clean no-op once #399 lands on test.

@plind-junior

Copy link
Copy Markdown
Collaborator

LGTM!

@plind-junior plind-junior merged commit 453869f into vouchdev:test Jul 6, 2026
8 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

adapters agent host adapters and install manifests mcp mcp, jsonl, and http surfaces size: M 200-499 changed non-doc lines tests tests and fixtures

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants