P1.5.3 — Codex Adapter — Step 3 Execution Packet

Round: R92, Wave 3 (parallel slice 2/3) — p1-5-3-codex-adapter Base SHA: 89adef66 Step: 3 of 5 (packet — gates implementation) Author tier: T3 executor


§1. Implementation sequence

A single commit at Step 4 ships:

  1. src/domains/router/adapters/codex.ts — adapter implementation
  2. src/__tests__/domains/router/adapters/codex.test.ts — parity tests

Both files are new. No existing source file is modified.

The implementation order within codex.ts:

Block 1  Header docblock (matches Claude adapter style; cites this packet)
Block 2  Imports (claude.js for shared types; nothing else)
Block 3  Constants (CODEX_API_BASE, CODEX_CHAT_COMPLETIONS_PATH,
         DEFAULT_CODEX_MODEL, DEFAULT_MAX_TOKENS, MAX_RETRIES, BASE_DELAY_MS)
Block 4  Error classes (CodexConfigError, CodexApiError)
Block 5  Public types (CodexCompletionOptions; re-export CompletionResult,
         AnthropicTool from '../../integrations/claude.js')
Block 6  Internal helpers (isRetryable, sleep, tryParseJson,
         normalizeFinishReason, translateToolsToOpenAi, projectToolCalls,
         buildCodexRequestBody, parseCodexResult)
Block 7  Core retry loop (attemptWithRetryCodex)
Block 8  Public API (createCodexCompletion, createCodexCompletionWithTools)

The implementation order within the test file:

Group A   Fixtures + mock helpers (FAKE_KEY, FAKE_MODEL, FAKE_PROMPT,
          SUCCESS_RESPONSE, TOOL_USE_RESPONSE, SAMPLE_TOOL,
          makeMockFetch, makeSilentLogger, makeInstantDelay, baseOptions)
Group B   createCodexCompletion — success path
Group C   createCodexCompletionWithTools — success path + tool-use mapping
Group D   API key validation
Group E   Retry logic
Group F   Finish-reason normalisation (table-driven)
Group G   Base URL override

§2. Test inventory (target 10–13 tests)

# Group Test Asserts
1 B returns CompletionResult with correct fields content, model, promptTokens, completionTokens, stopReason, latencyMs
2 B POSTs to /chat/completions with Authorization Bearer header URL ends with /chat/completions; Authorization: Bearer <key>; no x-api-key; no anthropic-version
3 B request body shape: model + max_tokens + messages array model present, max_tokens present, messages = [{role:’user’,content:prompt}]
4 B system prompt prepended as first message when present messages = [{role:’system’,…}, {role:’user’,…}]
5 C translates AnthropicTool[] → OpenAI tools nested under function key request.tools[0].type=’function’, request.tools[0].function = {name,description,parameters}
6 C empty tools array → no tools key in body (degrades to plain) 'tools' in body === false
7 C tool_calls response → content is JSON-stringified Anthropic-shape array content parses to [{type:'tool_use', id, name, input:{...}}]; stopReason=’tool_use’
8 D missing COLIBRI_CODEX_API_KEY → CodexConfigError with code rejects with instanceof CodexConfigError AND code === 'CODEX_CONFIG_ERROR'
9 D injected apiKey overrides process.env request Authorization header uses injected key
10 E 429 retry with exponential backoff (3 calls, 2 delays of 100/200) 3 fetch calls, 2 delayFn calls in [100, 200]
11 E retries exhausted → CodexApiError with CODEX_RETRIES_EXHAUSTED rejects; code matches; status is last 429
12 F normalizeFinishReason maps all 5 Codex values to Anthropic vocabulary table-driven via it.each: ‘stop’→’end_turn’, ‘tool_calls’→’tool_use’, ‘length’→’max_tokens’, ‘content_filter’→’content_filter’, missing→’unknown’
13 G baseUrl override changes endpoint request URL = ${override}/chat/completions

Total: 13 tests. Within the dispatch packet’s 5–10 floor (we exceed by 3 for the tool-use mapping coverage delta — justified in audit §10).


§3. Lint posture

eslint-config for this repo is opinionated. The Claude adapter is already lint-clean — the Codex adapter clones its conventions:

  • eslint-disable @typescript-eslint/no-explicit-any on the parseResult helper (Codex response shape is dynamic JSON).
  • eslint-disable no-constant-condition on the retry loop’s while (true).
  • All Record<string, unknown> for dynamic JSON manipulation.
  • No as any casts outside helper internals (the linter forbids them).

§4. Build posture

TypeScript strict mode is on. The contract types are public; their exports must compile under noImplicitAny and strictNullChecks.

Compile target: dist/domains/router/adapters/codex.js + .d.ts.


§5. Verification evidence layout (forward-look for Step 5)

The verification doc at Step 5 will contain:

  1. Test run log (last 50 lines of npm test)
  2. Test count delta vs base (3153 + 13 = 3166 expected)
  3. Build log (npm run build clean)
  4. Lint log (npm run lint clean)
  5. Acceptance criteria checklist (full §11 from audit, checked)
  6. Tool-use mapping evidence (test 5 + test 7 outputs)
  7. git diff origin/main..HEAD --stat output
  8. Confirmation that src/domains/router/index.ts is byte-identical to base (via git show origin/main:src/domains/router/index.ts | diff - src/domains/router/index.ts)

§6. Risk + rollback

  • The branch is feature/p1-5-3-codex-adapter. Rollback = git branch -D and gh pr close --delete-branch.
  • No DB migration. No env-schema change. No MCP-surface change. No router fold-in.
  • Worst case: tests fail at Step 4. Then iterate within Step 4 without committing partial work — Step 4 commits only the green state.

§7. Step 3 exit gate

The packet is complete. Implementation (Step 4) may proceed.


Commit message: packet(p1-5-3-codex-adapter): execution plan


Back to top

Colibri — documentation-first MCP runtime. Apache 2.0 + Commons Clause.

This site uses Just the Docs, a documentation theme for Jekyll.