fix(server): Codex V2 runs skills typed with any currency sigil - #13461
Conversation
The composers accept any currency symbol as the skill sigil (`€review`), and V1 Codex rewrote it to `$` before sending. The V2 Codex adapter sent the text unchanged, so Codex, which only parses `$name`, never invoked the skill. Export the prompt-side skill mention pattern from composerInlineTokens, derive the composer chip regex from the same source, and canonicalize the sigil in the V2 Codex input builder for both turn/start and turn/steer. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Thread transfer impact✅ Thread transfer remains within every enforced ceiling.
Baseline: unavailable · PR result: Scenario and decoded snapshot size10 historical turns, 5 command tools per turn, 878.9 KiB retained MCP result per historical turn, and a 1.05 MiB retained result in the measured turn.
Updated in place by a trusted workflow. PR artifacts are strictly validated and never executed. |
ApprovabilityVerdict: Approved at Macroscope's review found this PR approvable — This is a focused Codex V2 bug fix that restores execution of explicitly typed skill mentions while preserving ordinary currency amounts and existing dollar syntax. The production change is localized and covered by direct and replay tests for both turn start and steering. Notes:
You can add or adjust custom eligibility rules. Learn more. |
…tTurn and steerTurn The table test only called codexSkillMentionText, so dropping the call from toCodexInput left every test green. Replay a scripted turn that sends `€review do it` and steers with `then £ship it`, and expect the outbound turn/start and turn/steer frames to carry `$` mentions. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Dismissing prior approval to re-evaluate 008162e
7a7339a
into
t3code/codex-turn-mapping
Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
The web and mobile composers let users trigger a skill with any currency symbol, not just
$(#12098), so€reviewshows up as a skill chip. V1 Codex rewrote the sigil to$before sending the turn. The V2 Codex adapter sent the text unchanged. Codex only parses$nameas a skill mention (TOOL_MENTION_SIGILincodex-rs/skills/src/mentions.rsat rust-v0.156.1), so on V2€reviewreached Codex as plain text and the skill was never invoked.What changed
packages/shared/src/composerInlineTokens.tsnow exportsSKILL_MENTION_PATTERN, the prompt-side skill mention regex. The composer's chip regex is built from the same source. The two differ only at the end: the chip regex needs whitespace after the name, so a half-typed$reat the cursor doesn't become a chip. The prompt pattern also matches at end of text, because a sent prompt can end with a mention. This is the same(?=\s|$)variant that V1 Codex, Claude and Cursor already copy locally.CodexAdapterV2.tsaddscodexSkillMentionText, which does the same$1$$$2rewrite as V1.toCodexInputapplies it to the user's message text before attachment context is appended. That covers bothturn/startandturn/steer. Currency amounts (€20,€5k,€100M,€1e6) and glued tokens (5€review) don't match and stay prose.The Claude and Cursor drivers and the V1 runtime still have their own local copies of the pattern. I left them alone to keep this PR focused, and because V1 is being removed separately.
Verification
vp test run src/orchestration-v2/Adapters/CodexAdapterV2.test.ts src/orchestration-v2/testkit/CodexReplayFixtures.integration.test.ts(apps/server): 118 passed. The new table test covers€review do it→$review do it,£ship, a mid-sentence¥review, a mention after a newline, the astral𑿝, digit-leading€2spec, unchanged$review, and prose left alone:costs €20,€5k,€100M/€1e6,5€review.startTurnwith€review do itandsteerTurnwiththen £ship it, and expects the outboundturn/startandturn/steerframes to carry$review do itandthen $ship it. Revert check: withtoCodexInputreverted totext: turnInput.message.text, this test fails withCodexAppServerReplayFrameMismatchErroronturn/start(received€review do it), and it passes with the fix.turn/startorturn/steerinput with a currency-prefixed token, so no transcript changes. The fixture suite passes unchanged.vp test run src/composerInlineTokens.test.ts src/composerTrigger.test.ts(packages/shared): 45 passed.vp test run src/composer-editor-mentions.test.ts(apps/web): 34 passed. These cover the chip regex that is now derived from the shared source.tsc --noEmitfor apps/server and packages/shared: no errors.vp linton the touched files: no new warnings.codex exec --ephemeral0.156.1 in a temp repo with a.agents/skills/t3probeskill. For$t3probe, Codex injected the skill and the model answered with the skill's marker directly, with no tool calls. For€t3probe, the skill was not injected. The model guessed the intent from the skill list and ransedto read the SKILL.md itself (it also picked up an unrelated user skill). So€only works when the model happens to guess, which confirms$is the only sigil Codex parses.Model: Claude Opus 5.5 (Claude Code)
🤖 Generated with Claude Code