Skip to content

System prompt relocation in v1.4.8 degrades instruction-following in long conversations #154

@ihabwahbi

Description

@ihabwahbi

Summary

The v1.4.8 change (#148) relocates all non-core system prompt entries to the first user message to work around Anthropic's OAuth 400 rejection. While this fixes the immediate API error, it introduces a meaningful regression in instruction-following quality, particularly in long conversations.

Problem

The relocated content includes the entire OpenCode system prompt (~30K+ chars):

  • OpenCode's agent instructions (tone/style, tool usage policy, task management, code reference format)
  • Environment metadata (working directory, git status, platform)
  • Skills block (available skill descriptions)
  • User instruction files (AGENTS.md / CLAUDE.md contents)
  • Per-message system prompts

This content is prepended to the first user message via:

const firstUser = parsed.messages.find((m) => m.role === "user")

As the conversation grows, these instructions drift further from the current exchange:

Turn 1:   [system] [user₁ + instructions] → near the latest context
Turn 5:   [system] [user₁ + instructions] [asst] [user₂] ... [user₅] → drifting
Turn 30:  [system] [user₁ + instructions] [asst] [user₂] ... [user₃₀] → buried

Why this matters

System prompts occupy a privileged architectural position in Claude — they're always attended to regardless of context length. User messages don't have this property. They're subject to the "lost in the middle" effect, where content at the start of a long context receives less attention than content near the end.

In practice, this means:

  • Tool usage policies become less reliably followed in long sessions
  • Coding conventions from AGENTS.md/CLAUDE.md fade in influence
  • Tone/style guidelines may be ignored as the conversation grows
  • Prompt cache efficiency is reduced — system prompts cache well because they're static; instructions stuffed into user messages are interleaved with per-turn content that changes

The only thing that retains full system-level priority is the one-sentence Claude Code identity prefix.

Possible alternatives

Some ideas for fixing the OAuth rejection without sacrificing instruction priority:

  1. Split the system prompt into identity + billing (kept) and instructions (kept separately with the identity prefix prepended) — if the API check is specifically about entries that don't look like Claude Code's system prompt, prefixing each system entry with a recognized header might pass validation.

  2. Inject instructions into the most recent user message instead of the first — this doesn't solve the architectural privilege issue but avoids the "lost in the middle" problem by keeping instructions near the active context.

  3. Use a hybrid approach — keep critical instructions (tool policy, AGENTS.md) in system with a format that passes validation, and only relocate the less critical metadata to user messages.

Environment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions