docs(codex): pivot first-class-integration design to CLI-native channels - #1427
Conversation
… channels
Rewrites .genie/brainstorms/codex-first-class-integration/DESIGN.md to
reflect Felipe's settled directives from the 2026-04-27 brainstorm:
- CLI-first culture; Claude Code does not load MCP servers as plugins.
- Channels SEMANTICS yes (envelope), MCP TRANSPORT no.
- Genie absorbs the channel-server role externally; external integrations
(telegram, webhook, discord, omni/whatsapp) live as genie subcommands or
external processes, never as Claude plugins.
- No tmux send-keys for runtime delivery (spawn-time init still legitimate).
- Single substrate, multiple sources attribute via structured envelope:
<channel source="X" from="Y" k="v">body</channel>
- Codex hook bridge (PR #1424) is the codex receive pipe; PR B in the ladder
fills the missing UserPromptSubmit handler.
Captures the full PR ladder A through F, the empirical proof of native
delivery (hookbridge-test session), and the migration path for in-flight
codex agents.
Supersedes the morning-2026-04-27 SDK-driven version of the same file.
|
Important Review skippedAuto reviews are disabled on base/target branches other than the default branch. Please check the settings in the CodeRabbit UI or the ⚙️ Run configurationConfiguration used: Path: .coderabbit.yaml Review profile: ASSERTIVE Plan: Pro Run ID: You can disable this status message by setting the Use the checkbox below for a quick retry:
✨ Finishing Touches🧪 Generate unit tests (beta)
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. Comment |
There was a problem hiding this comment.
Code Review
This pull request introduces a design for integrating Codex into Genie using CLI-native channels and a hook-based delivery substrate, replacing legacy tmux-based methods. The proposed architecture utilizes a structured XML-like envelope for message attribution across different sources and outlines a multi-wave implementation plan. Feedback on the design suggests updating the delivery diagrams to reflect signature changes, implementing body escaping for the channel envelope to prevent parsing breakages, and ensuring that pending messages are not marked as read if a delivery hook times out.
| │ → Claude Code reads on next turn via SendMessage tool │ | ||
| │ │ | ||
| │ PATH 2 — codex recipients: │ | ||
| │ mailbox.send(repo, from, to, body) │ |
There was a problem hiding this comment.
The diagram for PATH 2 shows the legacy mailbox.send signature. It should be updated to include the source and meta parameters introduced in PR A to maintain consistency with the architectural changes described later in the document.
| │ mailbox.send(repo, from, to, body) │ | |
| │ mailbox.send(repo, from, to, body, {source, meta}) │ |
|
|
||
| | Decision | Rationale | | ||
| |----------|-----------| | ||
| | **Channel envelope** = `<channel source="X" from="Y" k="v">body</channel>` rendered into the message text on delivery | Captures provenance without schema churn for downstream consumers. Claude/codex agents see human-readable XML-ish text and can choose to react to specific sources. Round-trippable for debugging. | |
There was a problem hiding this comment.
The proposed XML-like envelope lacks a specification for escaping the body content. Without escaping (e.g., using CDATA or entity encoding), a message body containing the string will break the envelope structure and potentially confuse the agent's parser. Consider adding a note about escaping the body.
| | **Channel envelope** = `<channel source="X" from="Y" k="v">body</channel>` rendered into the message text on delivery | Captures provenance without schema churn for downstream consumers. Claude/codex agents see human-readable XML-ish text and can choose to react to specific sources. Round-trippable for debugging. | | |
| | **Channel envelope** = <channel source="X" from="Y" k="v">body</channel> (with body escaped) rendered into the message text on delivery | Captures provenance without schema churn for downstream consumers. Claude/codex agents see human-readable XML-ish text and can choose to react to specific sources. Round-trippable for debugging. | |
| | Risk | Severity | Mitigation | | ||
| |------|----------|------------| | ||
| | Channel envelope breaks downstream parsers that assume plain text | Low | Source defaults to `'agent'` → plain body; envelope only wraps when source is set explicitly. Existing peer-agent traffic is untouched. | | ||
| | Codex hook can timeout if mailbox query is slow | Low | Hook timeout is 15s (PR #1424). PG queries on `mailbox WHERE to_worker = X AND read = false` are sub-millisecond. Still: budget the hook to ≤500ms; bail with empty additionalContext on timeout. | |
There was a problem hiding this comment.
In the timeout mitigation for the Codex hook, it is important to clarify that if the handler bails and returns an empty additionalContext, the pending messages must not be marked as read. This ensures they are preserved for the next turn rather than being lost.
| | Codex hook can timeout if mailbox query is slow | Low | Hook timeout is 15s (PR #1424). PG queries on `mailbox WHERE to_worker = X AND read = false` are sub-millisecond. Still: budget the hook to ≤500ms; bail with empty additionalContext on timeout. | | |
| | Codex hook can timeout if mailbox query is slow | Low | Hook timeout is 15s (PR #1424). PG queries on mailbox WHERE to_worker = X AND read = false are sub-millisecond. Still: budget the hook to ≤500ms; bail with empty additionalContext on timeout (do not mark messages as read). | |
… channels (#1427) Rewrites .genie/brainstorms/codex-first-class-integration/DESIGN.md to reflect Felipe's settled directives from the 2026-04-27 brainstorm: - CLI-first culture; Claude Code does not load MCP servers as plugins. - Channels SEMANTICS yes (envelope), MCP TRANSPORT no. - Genie absorbs the channel-server role externally; external integrations (telegram, webhook, discord, omni/whatsapp) live as genie subcommands or external processes, never as Claude plugins. - No tmux send-keys for runtime delivery (spawn-time init still legitimate). - Single substrate, multiple sources attribute via structured envelope: <channel source="X" from="Y" k="v">body</channel> - Codex hook bridge (PR #1424) is the codex receive pipe; PR B in the ladder fills the missing UserPromptSubmit handler. Captures the full PR ladder A through F, the empirical proof of native delivery (hookbridge-test session), and the migration path for in-flight codex agents. Supersedes the morning-2026-04-27 SDK-driven version of the same file.
Summary
Rewrites
.genie/brainstorms/codex-first-class-integration/DESIGN.mdto reflect the settled architectural framing from the 2026-04-27 codex+channels brainstorm. Supersedes the morning version that proposed an@openai/codex-sdkdriver — Felipe rejected the SDK-primary path in favor of CLI-native primitives.Key shifts
<channel source="X" meta_k=v>body</channel>); reject the MCP wire.~/.claude/teams/<team>/inboxes/<agent>.json) and the same PG mailbox that peer agents use.~/.codex/config.tomltogenie hook dispatch. The missing handler (PR B in the ladder) reads PG mailbox onUserPromptSubmitand returnsadditionalContext.PR ladder captured in the design
mailbox.send+NativeInboxMessage;genie inbox listsource renderingUserPromptSubmithandler reads PG mailbox, returnsadditionalContextclaude-code.ts:deliver(omni→claude) from tmux send-keys to native inbox +source='whatsapp'claude-code.ts:injectNudgefrom tmux send-keys to native inbox +source='system'protocol-router.ts:injectToTmuxPaneafter metric confirms zero trafficPR A is in-flight on
feat/channel-envelope-inboxin parallel with this docs PR.Empirical proof preserved in the doc
The 2026-04-27 hookbridge-test session demonstrated end-to-end native delivery (
genie send→ PG mailbox → codex inbox-list →genie send pongreply) with zero tmux send-keys and zero MCP. The doc cites it as the validation that the substrate already works forsource='agent'; PRs A–F generalize that to all source kinds.Test plan
This is docs-only.
scripts/wishes-lint.ts) accepts_No brainstorm — direct wish_stub for any wish that won't have a brainstorm; this PR doesn't change the linter, only refreshes a brainstorm.🤖 Generated with Claude Code