OpenClaw incident learning

Same Codex subscription. Different agent runtime.

The Twitter curator did not become reliable because we changed the model or abandoned OAuth. It became reliable because OpenClaw stopped delegating the long-running agent loop to the Codex app-server harness and began owning that loop itself.

The working route still uses the ChatGPT/Codex subscription.

openai/gpt-5.6-terra + OpenAI OAuth stayed the same. Only the execution runtime changed from codex to openclaw for the dedicated curator agent.

Architecture

Where the fragile boundary disappeared

Now: OpenClaw embedded runtime with Codex-auth transport Per-agent policy: agentRuntime.id = "openclaw"
Cron schedulerTargets the dedicated twitter-curator agent.
🦞OpenClaw embedded loopOne owner for prompt, tools, session, retries and final result.
No app-server bridgeThe unreliable extra lifecycle boundary is bypassed.
🔐OpenAI OAuth profileRead-through from main; still subscription-backed.
🧠GPT-5.6 TerraSame selected model, reached through the internal Codex-auth transport.
🛠Native tool loopExec, process and message results stay inside OpenClaw’s loop.
💬Discord + ackOne complete briefing, followed by tweet acknowledgement.

Why this is stable: OpenClaw now owns the entire agent-turn state machine and produces the final cron result directly. OAuth supplies access, but app-server no longer mediates the long-running tool lifecycle.

Root-cause model

What failed—and what did not

The exact upstream app-server defect was not proven. What we isolated was the failing boundary: completion transport and lifecycle reconciliation under a long, tool-heavy cron turn.

Evidence pointed at the runtime bridge

The curator could fetch data, sometimes post partial content, and run successfully through another provider. Failures clustered around missing or empty final output after long Codex-harness runs.

“no JSON output” “empty result” heartbeat returned no JSON

These components were exonerated

OAuth: valid and subscription-backed. Model: Terra answered a smoke test. Discord: message-tool delivery worked. Curator scripts: returned usable data. LiteLLM: was not needed for the working path.

Not an OAuth outage Not a Discord failure Not model quality Not an OpenRouter requirement
Why it works

Four design changes made the difference

1

One owner for the turn lifecycle

OpenClaw now owns tool calls, polling, model continuation, final text, delivery state and cron completion.

2

OAuth was separated from runtime choice

The subscription credential is only the authentication mechanism. It does not require the Codex app-server harness.

3

The change was isolated per agent

Only twitter-curator uses the embedded runtime. Main chat, heartbeat, subagents and HN keep their existing policies.

4

Paid fallback was disabled

An empty fallback list makes failures visible and prevents silent OpenRouter credit consumption.

5

The same workspace was retained

The agent still sees the Twitter skill, editorial policy, scripts and credentials it needs, while session state lives separately.

6

We tested layers before switching cron

Auth visibility, model access, runtime identity and a harmless smoke turn all passed before the real curator was moved.

Before vs now

The operational difference

Before

  • Twitter cron ran as the shared main agent.
  • openai/gpt-5.6-terra selected the codex app-server harness.
  • Long tool runs crossed an additional streamed completion bridge.
  • Missing final result was interpreted as a failed cron run.
  • OpenRouter workaround restored reliability but consumed credits.

Now

  • Twitter cron targets a dedicated twitter-curator agent.
  • The exact model uses agentRuntime.id = "openclaw".
  • OpenClaw’s embedded runner directly manages the tool loop.
  • OAuth still authenticates against the Codex subscription route.
  • No LiteLLM/OpenRouter fallback is used or billed.
Proof from the successful run

The logs confirm the intended path

provider: "openai"The call did not go through LiteLLM.
model: "gpt-5.6-terra"The selected Codex subscription model stayed unchanged.
agentRuntime.id: "openclaw"The embedded runtime owned the cron turn.
runner: "embedded"No external CLI or app-server runner was involved.
authMode: "auth-profile"The existing OAuth profile supplied authentication.
fallbackUsed: falseNo OpenRouter credits were consumed.
delivered: trueThe Discord message tool completed successfully.
summary: "HEARTBEAT_OK"The run and acknowledgement path finished cleanly.
Reusable playbook

How to diagnose this class of failure next time

1
Separate auth from executionAsk whether credentials fail, or whether the chosen harness fails after authentication.
2
Run a tiny smoke turnConfirm provider, model, auth mode and runtime before testing the full workflow.
3
Inspect the failure boundaryDistinguish model errors, tool errors, delivery errors and missing final-result errors.
4
Isolate with a dedicated agentUse per-agent model/runtime policy instead of changing the whole system.
5
Verify attributionRequire logs to show provider, model, runtime, session key, delivery and fallback state.
Future design rule

For long-running scheduled agents, prefer the runtime that directly owns the complete tool-and-result lifecycle. Treat OAuth, model selection and runtime selection as three independent configuration decisions.