Skip to content

Meridian reliability: make concurrent RLM subagents production-safe over Claude Max #22

Description

@rynfar

Umbrella for making Meridian (the Claude Agent SDK proxy at rynfar/meridian) a production-safe provider for Prime Agent, including concurrent RLM subagents. Meridian's README currently rates Prime Agent "single-agent verified" with these failure modes: overload amplification, expensive cache churn after fresh-session replay, loss of child-task context during recovery, undelivered tool envelopes, incomplete parent-to-child cancellation.

Measured evidence (Meridian telemetry, real prime traffic 2026-08-17/18)

  • prime adapter averaged 65.8% cache hit vs 95.1% for OpenCode; 212 successful requests ran below 20% cache hit and wrote ~12M cache-creation tokens.
  • Repeated cache_miss: Cache hit rate 0% on resume (expected >50%) diagnostics on continuations.
  • Resume-failure cascade observed: "You've hit your session limit" on resume, then the same request retried resume=false (full fresh replay) one second later; a dozen requests failed in ~15s.
  • ENVELOPE VIOLATION [undelivered_tool_use] on the ipython tool, two coinciding with upstream 529s.

Root causes on the Prime Agent side (verified in this repo)

  1. Provider identity is unstable and collapses for inline subagents. The Meridian extension stamps metadata.user_id from ctx.sessionManager.getSessionId() in before_provider_request. Inline RLM children reuse the parent's extension runner (packages/coding-agent/src/core/agent-session.ts inline child construction copies onPayload: this.agent.onPayload; the root streamFn/onPayload in packages/coding-agent/src/core/sdk.ts closes over the root extensionRunnerRef), so every inline child request is stamped with the parent's session key. Interleaved parent/child histories on one proxy session key force a fresh-session replay nearly every round. SessionManager.sessionId also mutates on fork/branch/load, and compaction/branch-summarization/refinement completeSimple calls bypass onPayload entirely (unkeyed). → feat(agent): stable child-scoped provider identity for RLM subagents, side questions, and auxiliary requests #23
  2. Retries multiply against an overloaded upstream. Session-level auto-retry (_isRetryableError treats every stopReason === "error" as retryable; fixed 2s/4s/8s backoff, no jitter, no Retry-After) stacks on the Anthropic SDK's own maxRetries, ≈ 4 × (1 + sdkRetries) requests per failed turn per agent, times N concurrent children. maxRetryDelayMs is declared and threaded but never read by any provider (dead code). → fix(agent): kind-aware retry with Retry-After and jitter; implement dead maxRetryDelayMs; stop SDK-times-session retry multiplication #24
  3. User-facing cancel does not reach children. requestAbort() does not call _cancelActiveRlmChildRuns() (only abort() does), and every user-facing cancel path uses requestAbort(). Orphaned children keep consuming the Max subscription. → fix(agent): requestAbort() must cascade to active RLM child runs #25
  4. The cached prompt prefix is volatile. Mutable continual-harness state and Current date: are serialized into the system prompt; cache_control rides the last tool definition so tool-set changes/reordering invalidate the tool cache. → perf(agent): keep the prompt-cache prefix stable across harness updates, date flips, and tool refreshes #26

Meridian-side work (tracked in rynfar/meridian, cross-linked)

Sequencing

Per PYLON.md, each item needs an upstream-overlap check and a .pylon/features.yaml decision before landing.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions