agent-pages

OpenClaw Scheduled Agent Reliability: Technical Deep Dive

Download

OpenClaw Twitter Curator: Technical Deep Dive

Executive summary

The Twitter Curator became reliable without changing the selected model or abandoning the ChatGPT/Codex subscription.

The material change was the agent runtime:

The failures were therefore not evidence that the subscription, model, Discord integration, or curator scripts were broken. The strongest evidence isolates the failure to the extra app-server execution/completion boundary used by the old route.

Epistemic status: the failing boundary is strongly isolated by controlled comparison, but the precise upstream defect inside the app-server integration was not independently proven.


1. Keep the layers separate

Several independent choices were previously being treated as though they were one setting:

Layer Selected value Responsibility
Provider openai Model catalog, auth selection, and request transport
Model gpt-5.6-terra Reasoning and text generation
Authentication OpenAI OAuth profile ChatGPT/Codex subscription entitlement
Agent runtime codex or openclaw Owns the agent turn and tool lifecycle
Scheduler OpenClaw cron Starts isolated scheduled runs
Delivery Discord message tool Publishes the briefing

The key lesson is:

OAuth answers “who pays and which entitlement is used?” Runtime selection answers “which software owns the agent loop?”

Using a Codex OAuth profile does not require the Codex app-server harness. OpenClaw 2026.7.1 can use its embedded runtime while routing an openai/* model through its internal Codex-auth transport.


2. The old execution path

The shared default agent selected openai/gpt-5.6-terra, whose effective runtime policy was codex.

flowchart LR
    C[Cron scheduler] --> O[OpenClaw cron runner]
    O --> H[Codex app-server harness]
    H --> A[OpenAI OAuth]
    A --> M[GPT-5.6 Terra]
    M --> H
    H --> R[Streamed event and completion reconciliation]
    R --> O
    O --> T[Exec / process / message tools]
    T --> D[Discord]

    classDef risk fill:#4a1f2a,stroke:#ff6b78,color:#fff;
    class H,R risk;

What the app-server boundary added

OpenClaw prepared the cron turn, but a separate harness owned the Codex turn lifecycle. The integration then had to:

  1. start or attach to an app-server session;
  2. forward the prepared prompt and runtime context;
  3. consume streamed events;
  4. coordinate tool execution and continuation;
  5. detect the final assistant result;
  6. convert that result into the shape expected by the cron runner;
  7. report completion, delivery, and heartbeat status.

This architecture can work for ordinary turns. The Twitter Curator was a more demanding case:

Each extra tool continuation increases the importance of correct session and completion-state reconciliation.


3. Observed symptoms

The recurring errors were variants of:

These messages describe what the cron runner received, not necessarily what the model or curator script had done internally.

Why this distinction matters

Some failed runs had already produced useful work or reached later workflow stages. That pattern is inconsistent with a simple authentication rejection at request start.

sequenceDiagram
    participant Cron as Cron runner
    participant Harness as Codex app-server harness
    participant Model as GPT-5.6 Terra
    participant Tools as Curator tools

    Cron->>Harness: Start prepared agent turn
    Harness->>Model: Prompt + context
    Model->>Tools: Read policy / run heartbeat check
    Tools-->>Model: Data or tool result
    Model->>Tools: Optional Discord send / acknowledgement
    Tools-->>Model: Success
    Note over Harness,Cron: Fragile final-event reconciliation boundary
    Harness--xCron: Missing or unusable final result
    Cron->>Cron: Report “no JSON output” / empty result

Components ruled out by evidence

Suspected component Evidence against it being the primary cause
OpenAI OAuth The OAuth profile was valid and later completed both a smoke test and the real cron run.
GPT-5.6 Terra The same model succeeded after only the runtime changed.
Twitter curator scripts The scripts returned usable batches through the working OpenRouter route and the new OAuth route.
Discord The message tool successfully published the final briefing.
Cron scheduling The job consistently started at the expected times.
LiteLLM It was neither required nor used by the final working request path.

4. Why the OpenRouter workaround worked

The temporary route was:

litellm/openai/gpt-5.6-terra

That route behaved like a conventional OpenAI-compatible model provider. OpenClaw owned the embedded tool loop and received normal provider responses through LiteLLM. It avoided the Codex app-server harness, so it also avoided the fragile completion boundary.

flowchart LR
    C[Cron] --> O[OpenClaw embedded loop]
    O --> L[LiteLLM]
    L --> OR[OpenRouter]
    OR --> M[GPT-5.6 Terra]
    M --> O
    O --> D[Discord + acknowledgement]

This proved that the curator workflow itself was viable, but it introduced usage-based OpenRouter billing and quickly depleted credits.

The workaround was diagnostically valuable because it changed the runtime/provider transport while keeping the high-level task nearly identical.


5. The final architecture

A dedicated curator agent now overrides only the runtime for the exact OpenAI model.

flowchart LR
    C[Cron scheduler] --> TA[Dedicated curator agent]
    TA --> E[OpenClaw embedded runtime]
    E --> OA[Internal Codex-auth transport]
    OA --> P[OpenAI OAuth profile]
    P --> M[GPT-5.6 Terra]
    M --> E
    E --> X[Exec and process tools]
    E --> MSG[Discord message tool]
    MSG --> ACK[Tweet acknowledgement]
    E --> RES[Final cron result]

    B[Codex app-server harness]:::bypassed
    E -. bypasses .-> B

    classDef good fill:#13372c,stroke:#55d68b,color:#fff;
    classDef bypassed fill:#202733,stroke:#667085,color:#9aa4b2,stroke-dasharray:5 5;
    class E,OA,MSG,RES good;

Effective per-agent configuration

{
  "agents": {
    "list": [
      {
        "id": "default-agent",
        "default": true
      },
      {
        "id": "curator-agent",
        "workspace": "/path/to/shared-workspace",
        "agentDir": "/path/to/agent-state/curator",
        "model": {
          "primary": "openai/gpt-5.6-terra",
          "fallbacks": []
        },
        "thinkingDefault": "medium",
        "models": {
          "openai/gpt-5.6-terra": {
            "agentRuntime": {
              "id": "openclaw"
            }
          }
        }
      }
    ]
  }
}

The cron job is pinned separately:

agentId: curator-agent
model: openai/gpt-5.6-terra
thinking: medium
fallbacks: []

6. Why the per-agent override wins

OpenClaw resolves runtime policy after resolving the provider and model.

For this deployment, the important precedence is:

  1. exact entry in agents.list[].models["provider/model"].agentRuntime;
  2. exact entry in agents.defaults.models["provider/model"].agentRuntime;
  3. exact provider model definition;
  4. provider wildcard policy;
  5. provider-wide policy;
  6. automatic runtime claim or OpenClaw compatibility fallback.
flowchart TD
    A[Resolve provider + model] --> B{Per-agent exact runtime?}
    B -- Yes --> C[Use curator-agent policy: openclaw]
    B -- No --> D{Default exact runtime?}
    D -- Yes --> E[Use global policy: codex]
    D -- No --> F{Provider/model policy?}
    F -- Yes --> G[Use configured provider policy]
    F -- No --> H[Auto claim or OpenClaw fallback]

Therefore:

This is safer than changing the provider or global model policy.


7. OAuth inheritance

Agents have separate state directories. The dedicated agent uses:

/path/to/agent-state/curator

The OAuth refresh material remains in the real default-agent store. Because that agent remains the configured default, OpenClaw supports read-through OAuth inheritance:

  1. the secondary agent requests an openai auth profile;
  2. its own store does not need a copied refresh token;
  3. OpenClaw reads the matching profile from the default-agent store;
  4. the freshest usable access token is adopted for the request;
  5. refresh material is not duplicated across agent stores.

The verification output showed:

openai effective=profiles:/path/to/default-agent/auth-store
profiles=1 (oauth=1)

This is why it was important to keep an explicit default-agent entry with default: true. If the new agent had accidentally become the default, the expected OAuth read-through behavior could have changed.


8. Successful run sequence

sequenceDiagram
    participant Scheduler as OpenClaw cron
    participant Agent as curator-agent
    participant Runtime as OpenClaw embedded runtime
    participant OAuth as OpenAI OAuth profile
    participant Terra as GPT-5.6 Terra
    participant Tools as Exec / process
    participant Discord as Discord message tool

    Scheduler->>Agent: Start isolated cron run
    Agent->>Runtime: Resolve openai/gpt-5.6-terra
    Runtime->>Runtime: Per-agent policy selects openclaw
    Runtime->>OAuth: Resolve subscription credential
    OAuth-->>Runtime: Valid auth-profile token
    Runtime->>Terra: Prompt + workspace context
    Terra->>Tools: Read GOAL.md and run check-heartbeat
    Tools-->>Terra: Candidate tweet batch
    Terra->>Terra: Filter, rank and compose briefing
    Terra->>Discord: Send one complete message
    Discord-->>Terra: Delivery succeeded
    Terra->>Tools: Acknowledge delivered tweet IDs
    Tools-->>Terra: Acknowledgement succeeded
    Terra-->>Runtime: HEARTBEAT_OK
    Runtime-->>Scheduler: status=ok, delivered=true

The same component now owns both the model continuation and the interpretation of tool results. There is no second harness required to reconstruct a final completion from an external streamed event lifecycle.


9. Evidence from the verified run

Smoke test

Field Observed value Meaning
Response OAUTH_RUNTIME_OK Model completed the requested turn
Provider openai Not routed through LiteLLM
Model gpt-5.6-terra Intended subscription model
Harness openclaw Embedded runtime selected
Runner embedded No CLI/app-server execution backend
Auth mode auth-profile OAuth profile used
Fallback false No paid fallback

Real curator run

Field Observed value
Status ok
Summary HEARTBEAT_OK
Provider openai
Model gpt-5.6-terra
Agent curator-agent
Runtime openclaw
Delivery delivered: true
Fallback used false
Session key prefix agent:<curator-agent>:cron:

The Discord screenshot showed one complete briefing with four selected tweets. No acknowledgement-failure warning appeared, which is consistent with a successful acknowledgement path.


10. Why the final design is safer

Failure isolation

The Twitter job can change runtime policy without affecting:

Cost isolation

fallbacks: [] is explicit. If subscription auth or the embedded route fails, the job fails visibly instead of silently consuming OpenRouter credits.

State isolation

The dedicated agent has its own session and agent state while sharing the established workspace:

Shared:
  /path/to/shared-workspace
  ├── AGENTS.md
  ├── Twitter curator skill
  ├── GOAL.md
  └── curator scripts/data

Separate:
  /path/to/agent-state/curator/
  ├── agent state
  └── sessions

This preserves the workflow’s inputs without mixing its run history with the default agent.


11. A reusable diagnostic method

When a scheduled agent fails, inspect each layer independently.

flowchart TD
    S[Scheduled job failed] --> A{Did authentication start?}
    A -- No --> A1[Inspect auth profile, expiry and entitlement]
    A -- Yes --> M{Did the model produce a turn?}
    M -- No --> M1[Inspect provider/model errors and limits]
    M -- Yes --> T{Did tools execute?}
    T -- No --> T1[Inspect tool schema, permissions and runtime]
    T -- Yes --> D{Did external delivery succeed?}
    D -- No --> D1[Inspect channel target and message-tool result]
    D -- Yes --> R{Did the runner receive a final result?}
    R -- No --> R1[Inspect harness/bridge completion lifecycle]
    R -- Yes --> OK[Inspect task-specific validation or acknowledgement]

Minimum evidence to collect

  1. provider and model;
  2. effective agentRuntime.id;
  3. auth mode and selected profile source;
  4. session key and owning agent;
  5. tool-call result, not just the model’s narration;
  6. delivery result;
  7. fallback usage;
  8. final runner status and summary.

Preferred rollout sequence

  1. validate configuration;
  2. verify agent/default routing;
  3. verify auth visibility for the target agent;
  4. run a harmless fixed-response smoke test;
  5. inspect session runtime attribution;
  6. switch the real cron;
  7. perform one manual real run;
  8. observe several scheduled runs;
  9. retain an explicit rollback command.

12. Rollback

The emergency rollback restores the known working paid route without deleting the dedicated agent:

{
  "job": "<curator-job>",
  "agentId": "<default-agent>",
  "model": "<paid-fallback-provider>/gpt-5.6-terra",
  "thinking": "medium",
  "fallbacks": []
}

Leaving the dormant agent state intact makes rollback fast and avoids destructive cleanup during an incident.


13. Durable lessons

  1. Provider, model, auth and runtime are separate architectural decisions.
  2. A successful provider workaround does not prove the original model was bad; it may only bypass a failing harness.
  3. “No output” errors often describe the observer’s state, not the actual amount of work completed downstream.
  4. Long-running tool agents stress lifecycle boundaries more than short chat turns.
  5. Prefer one component to own the complete tool/result state machine when reliability matters.
  6. Use per-agent runtime overrides to constrain blast radius.
  7. Disable automatic paid fallbacks when cost predictability is more important than silent continuity.
  8. Require runtime attribution in verification logs; checking only the model name is insufficient.

The final design works because it keeps the desired subscription and model while removing the unnecessary execution bridge that correlated with the failures.