Skip to content

fix(codex): read feature flags from a TOML parse, not a line scanner (#1295) - #1309

Merged
lidge-jun merged 1 commit into
devfrom
codex/260808-1295-toml-multiline-features
Aug 8, 2026
Merged

fix(codex): read feature flags from a TOML parse, not a line scanner (#1295)#1309
lidge-jun merged 1 commit into
devfrom
codex/260808-1295-toml-multiline-features

Conversation

@lidge-jun

@lidge-jun lidge-jun commented Aug 8, 2026

Copy link
Copy Markdown
Owner

Summary

Fixes #1295. codex features enable multi_agent_v2 writes enabled = true correctly, and OpenCodex read the file back and concluded it had not — so the dashboard toggle failed with multi_agent_v2 transition failed: codex feature command did not enable multi_agent_v2, and ocx v2 status kept printing OFF for a config.toml that codex features list reports as enabled. The Codex command was never at fault.

tomlTableBody ends a table at the first line matching /^\s*\[/, unaware of multi-line strings. A """ value whose prose begins with a bracketed tag cuts the table body mid-literal — and since codex features enable appends enabled at the end of the table, that key lands outside the extracted body. Key order alone decided the answer: with identical semantics, enabled before the string read true and after it read false.

The readers that answer these questions now use Bun.TOML.parse through a shared parsedTomlTable. Table form, boolean form, and inline-table form all fall out of the parse instead of each needing its own regex, and prose that merely looks like an assignment stops being one.

hasAgentsMaxThreads uses Object.hasOwn rather than checking the value: it gates a codex-rs boot refusal, so a present-but-unusable max_threads must still be detected even where the getter correctly declines to return it.

tomlTableBody itself is unchanged — only its comment. Twenty call sites consume its output, mostly by matching a regex against the returned text, so widening that text changes what all of them match. An earlier attempt at this fix made the scanner string-aware and thereby gave getAgentsEnabled, getAgentsMaxDepth, and getMaxConcurrentThreads three new wrong answers. It remains the fallback for a document Bun's parser rejects; that fallback is best-effort by construction and claims nothing about what Codex's own parser would accept, since they are separate implementations.

Two sibling readers had the same prose-as-assignment defect before this change, and are fixed alongside rather than left inconsistent with the reader that shares the file: isDefaultModeRequestUserInputEnabled, and the [agents] max_threads pair.

Verification

  • bun run test10048 pass / 7 skip / 0 fail across 627 files
  • bun test tests/codex-v2-gate.test.ts — 100 pass / 0 fail
  • bun run typecheck — clean
  • bun run privacy:scan — passed
  • Ablation: reverting only src/codex/features.ts fails 8 distinct tests

The tests are deliberately one hazard per test() block. Bundled as a single block they reported one failure under ablation, because Bun stops a block at its first failing expectation — so a later assertion could never run and still look covered. Split, the same ablation reports eight.

Covered: bracketed prose in a """ value, the same in a ''' value, key-order independence, prose that contains a literal enabled = true with no such key, a delimiter inside a comment, an escaped \""", a multi-line array, an array opening on the line after =, # inside a string, a header-shaped line inside a string, a following table's key never being read as this feature's, the unparseable-document fallback, and presence-vs-usability for max_threads.

Checklist

  • Scope stays focused and avoids unrelated cleanup.
  • Docs or release notes were updated when needed. (No documented behavior changes; this makes the reader agree with what codex features list already reports.)
  • Security-sensitive changes were reviewed for secrets, auth, and unsafe defaults. (Reads local config.toml feature flags only; no credential, auth, or workflow surface is touched, and nothing new is logged.)

Reported by @brunoflma, whose minimal three-file reproduction isolated the single variable and identified tomlTableBody directly — which is why this went straight to the scanner instead of through the Codex command.

Summary by CodeRabbit

  • Bug Fixes

    • Improved reading of feature and agent configuration from TOML, including multiline values, comments, arrays, and varied key ordering.
    • Added reliable fallback behavior for malformed or unsupported configuration files.
    • Improved handling of agent thread-limit settings, including distinguishing missing and invalid values.
  • Tests

    • Added regression coverage for TOML parsing, table boundaries, escaped content, and fallback scenarios.
  • Documentation

    • Expanded guidance on configuration scanning limitations and parser fallback behavior.

…1295)

`codex features enable multi_agent_v2` writes `enabled = true` correctly, and
OpenCodex then read the file back and concluded it had not — so the dashboard
toggle reported `codex feature command did not enable multi_agent_v2` and
`ocx v2 status` kept printing OFF for a config `codex features list` reports
as enabled.

`tomlTableBody` ends a table at the first line matching `/^\s*\[/`, with no
awareness of multi-line strings. A `"""` value whose prose begins with a
bracketed tag therefore cut the table body mid-literal, and because
`codex features enable` appends `enabled` at the END of the table, that key
landed outside the extracted body. Key order alone decided the answer:
identical semantics, `enabled` before the string read true, after it read
false.

The readers that answer these questions now use `Bun.TOML.parse` through
`parsedTomlTable`. Table form, boolean form, and inline-table form all fall
out of the parse rather than each needing its own regex, and prose that
merely looks like an assignment is no longer one. `hasAgentsMaxThreads` uses
`Object.hasOwn` rather than a value check: it gates a codex-rs boot refusal,
so a present-but-unusable key must still be detected even when the getter
declines to return it.

`tomlTableBody` itself is unchanged. Twenty call sites consume its output,
mostly by regex, so widening what it returns changes what all of them match
— an earlier attempt at this fix made the scanner string-aware and gave
`getAgentsEnabled`, `getAgentsMaxDepth`, and `getMaxConcurrentThreads` three
new wrong answers. It remains the fallback for a document Bun's parser
rejects, which is best-effort by construction and claims nothing about what
Codex's own parser would accept.

Two sibling readers had the same prose-as-assignment defect before this
change and are fixed alongside: `isDefaultModeRequestUserInputEnabled` and
the `[agents] max_threads` pair.

Reported by @brunoflma with a minimal three-file reproduction that isolated
the single variable, which is why this went straight to the scanner rather
than through the Codex command.
@coderabbitai

coderabbitai Bot commented Aug 8, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 035467d4-13d2-44eb-b5ed-df9aa2e435af

📥 Commits

Reviewing files that changed from the base of the PR and between e8ec8d1 and 493b418.

📒 Files selected for processing (2)
  • src/codex/features.ts
  • tests/codex-v2-gate.test.ts

📝 Walkthrough

Walkthrough

Changes

TOML feature readers

Layer / File(s) Summary
Parsed TOML support
src/codex/features.ts
The code validates Bun TOML parser results and retrieves top-level tables. The existing scanner documentation now describes its string-unaware fallback behavior.
Reader integration and regression coverage
src/codex/features.ts, tests/codex-v2-gate.test.ts
Feature flags and agents.max_threads use parsed TOML values before scanner fallback. Tests cover multiline strings, comments, arrays, malformed documents, table boundaries, and invalid thread values.

Estimated code review effort: 3 (Moderate) | ~20 minutes

Possibly related PRs

  • lidge-jun/opencodex#911: Both changes extend TOML parsing and feature-reader fallback logic in the same source and test files.
  • lidge-jun/opencodex#1209: Both changes improve TOML parsing and configuration readers in the same source and test files.

Suggested reviewers: chrisae9, wibias, ingwannu

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly identifies the main change: reading feature flags from parsed TOML instead of a line scanner.
Linked Issues check ✅ Passed The changes address issue #1295 by using parsed TOML with scanner fallback and adding regression tests for multiline strings and enabled-key detection.
Out of Scope Changes check ✅ Passed The parser updates, related readers, documentation, and regression tests support the stated issue and pull request objectives.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch codex/260808-1295-toml-multiline-features

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.

@chatgpt-codex-connector chatgpt-codex-connector Bot 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.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 493b418678

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread src/codex/features.ts
const parsed = parsedTomlTable(content, "agents");
if (parsed !== null) {
const value = parsed.max_threads;
return typeof value === "number" && Number.isInteger(value) && value >= 1 ? value : null;

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Reject TOML floats before returning max_threads

When max_threads is a TOML float whose JavaScript value is integral, such as max_threads = 1.0 or 1e3, Bun.TOML.parse returns 1 or 1000, so this Number.isInteger check accepts it. Codex deserializes this field as usize and rejects floats, while the previous lexical reader returned null; consequently, status and transition flows now treat an unusable setting as valid and may migrate it to another key. Preserve the TOML numeric kind or validate the original lexeme before returning the value.

AGENTS.md reference: src/AGENTS.md:L10-L10

Useful? React with 👍 / 👎.

@lidge-jun
lidge-jun merged commit b90b41f into dev Aug 8, 2026
23 checks passed
@lidge-jun
lidge-jun deleted the codex/260808-1295-toml-multiline-features branch August 8, 2026 18:24
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug Something isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant