[GH-ISSUE #59] [BUG] User intent gets overshadowed because prompt roles are mixed during runtime composition #50

Closed
opened 2026-02-27 15:38:08 +03:00 by kerem · 5 comments
Owner

Originally created by @DarKWinGTM on GitHub (Feb 23, 2026).
Original GitHub issue: https://github.com/NikkeTryHard/zerogravity/issues/59

What is happening

The core problem is simple:

Zerogravity mixes user-side prompt roles into one content stream.
At the same time, LS system instruction still exists as a stronger control layer.

Because of that, the user’s real intent can lose priority.
In practice, it can look like the model is not listening to the user consistently.


Why this matters

From the user perspective, this directly affects response quality:

  • user commands are followed inconsistently
  • answers drift away from the requested task
  • hallucination-like behavior appears in long or complex prompts

This is an intent-priority problem, not a UI/network issue.


How the current flow creates this issue

The runtime currently builds prompt input in a way that flattens role boundaries:

  1. Chat-completions input is built as one text output

    • src/api/completions.rs:85
    • src/api/completions.rs:93
  2. Both system|developer and user are pushed into the same parts list

    • src/api/completions.rs:122
    • src/api/completions.rs:128
  3. The final message is flattened into one string

    • src/api/completions.rs:162 (parts.join("\\n\\n"))
  4. In responses flow, instruction text is directly concatenated with user text

    • src/api/responses.rs:137
    • src/api/responses.rs:138 (format!("{inst}\\n\\n{user_text}"))

At the same time, MITM path still handles LS system instruction separately and rewrites request content:

  • src/mitm/modify.rs:64
  • src/mitm/modify.rs:96
  • src/mitm/modify.rs:297
  • src/mitm/modify.rs:308

This combination creates an imbalance: user-side intent gets flattened, LS-side control remains structurally strong.


Core impact

  • user intent is easier to be underweighted
  • system-layer behavior can dominate unexpectedly
  • output becomes less reliable for real task-following

Expected behavior

User-facing intent should remain clear and stable in priority.
Prompt role boundaries should not collapse in a way that makes user commands ambiguous.

Actual behavior

Current composition collapses role structure in user-side runtime input,
and this can cause user intent to be overshadowed in real interactions.


Scope of this report

This report is only about the structural prompt-semantics problem.
No workaround or fix proposal is included here.


Additional observed symptoms (from real usage)

  • Responses do not match the actual prompt request.
  • The model repeats the same explanation loop, even when that repeated explanation is not what was requested.
  • Example: after design/planning is already completed, the next prompt asks to execute the planned work; instead of executing, the model goes back to re-explaining the same starting points and does not proceed.
  • In practice this appears as confusion/hallucination-like behavior and task non-progression.

Usage context

This report is based on real usage in Claude Code CLI (Anthropic-compatible flow).

Flow diagram (Claude Code CLI)

Claude Code CLI request (user prompt + user-side system prompt)
→ Zerogravity API (/v1/messages, Anthropic-compatible)
→ Runtime prompt composition
→ user-side system/developer + user collapsed into one text stream
→ LS system instruction still handled as a separate control layer
→ model receives asymmetric prompt structure
→ user intent can be underweighted in some cases
→ repeated/off-target explanations and non-progression


Visual flow diagram

Claude Code CLI (Anthropic-compatible: /v1/messages)

User sends prompt
  → includes user request text
  → may include user-provided system/developer instruction
  ↓
Zerogravity runtime receives request
  ↓
Prompt assembly stage
  → user-side system/developer + user content are merged into one text stream
  ↓
LS system instruction path
  → LS system instruction is still handled as a separate control layer
  ↓
Model input becomes asymmetric
  → one merged user-side stream + one separate LS control layer
  ↓
Effective priority can skew toward LS/system layer
  ↓
User intent can be underweighted
  ↓
Observed symptoms
  → answer does not match asked prompt
  → repeats old explanation loop
  → does not execute the requested next step (non-progression)
Originally created by @DarKWinGTM on GitHub (Feb 23, 2026). Original GitHub issue: https://github.com/NikkeTryHard/zerogravity/issues/59 ### What is happening The core problem is simple: Zerogravity mixes user-side prompt roles into one content stream. At the same time, LS system instruction still exists as a stronger control layer. Because of that, the user’s real intent can lose priority. In practice, it can look like the model is not listening to the user consistently. --- ### Why this matters From the user perspective, this directly affects response quality: - user commands are followed inconsistently - answers drift away from the requested task - hallucination-like behavior appears in long or complex prompts This is an intent-priority problem, not a UI/network issue. --- ### How the current flow creates this issue The runtime currently builds prompt input in a way that flattens role boundaries: 1. Chat-completions input is built as one text output - `src/api/completions.rs:85` - `src/api/completions.rs:93` 2. Both `system|developer` and `user` are pushed into the same `parts` list - `src/api/completions.rs:122` - `src/api/completions.rs:128` 3. The final message is flattened into one string - `src/api/completions.rs:162` (`parts.join("\\n\\n")`) 4. In responses flow, instruction text is directly concatenated with user text - `src/api/responses.rs:137` - `src/api/responses.rs:138` (`format!("{inst}\\n\\n{user_text}")`) At the same time, MITM path still handles LS system instruction separately and rewrites request content: - `src/mitm/modify.rs:64` - `src/mitm/modify.rs:96` - `src/mitm/modify.rs:297` - `src/mitm/modify.rs:308` This combination creates an imbalance: user-side intent gets flattened, LS-side control remains structurally strong. --- ### Core impact - user intent is easier to be underweighted - system-layer behavior can dominate unexpectedly - output becomes less reliable for real task-following --- ### Expected behavior User-facing intent should remain clear and stable in priority. Prompt role boundaries should not collapse in a way that makes user commands ambiguous. ### Actual behavior Current composition collapses role structure in user-side runtime input, and this can cause user intent to be overshadowed in real interactions. --- ### Scope of this report This report is only about the structural prompt-semantics problem. No workaround or fix proposal is included here. --- ### Additional observed symptoms (from real usage) - Responses do not match the actual prompt request. - The model repeats the same explanation loop, even when that repeated explanation is not what was requested. - Example: after design/planning is already completed, the next prompt asks to execute the planned work; instead of executing, the model goes back to re-explaining the same starting points and does not proceed. - In practice this appears as confusion/hallucination-like behavior and task non-progression. --- ### Usage context This report is based on real usage in **Claude Code CLI** (Anthropic-compatible flow). ### Flow diagram (Claude Code CLI) Claude Code CLI request (user prompt + user-side system prompt) → Zerogravity API (`/v1/messages`, Anthropic-compatible) → Runtime prompt composition → user-side `system/developer` + `user` collapsed into one text stream → LS system instruction still handled as a separate control layer → model receives asymmetric prompt structure → user intent can be underweighted in some cases → repeated/off-target explanations and non-progression --- ### Visual flow diagram ```text Claude Code CLI (Anthropic-compatible: /v1/messages) User sends prompt → includes user request text → may include user-provided system/developer instruction ↓ Zerogravity runtime receives request ↓ Prompt assembly stage → user-side system/developer + user content are merged into one text stream ↓ LS system instruction path → LS system instruction is still handled as a separate control layer ↓ Model input becomes asymmetric → one merged user-side stream + one separate LS control layer ↓ Effective priority can skew toward LS/system layer ↓ User intent can be underweighted ↓ Observed symptoms → answer does not match asked prompt → repeats old explanation loop → does not execute the requested next step (non-progression) ```
kerem closed this issue 2026-02-27 15:38:08 +03:00
Author
Owner

@NikkeTryHard commented on GitHub (Feb 23, 2026):

zg report <trace-id> zg report

<!-- gh-comment-id:3944062961 --> @NikkeTryHard commented on GitHub (Feb 23, 2026): `zg report <trace-id>` `zg report`
Author
Owner

@DarKWinGTM commented on GitHub (Feb 23, 2026):

zg-trace-2026-02-23_06-16-15_gemini-3.1-pro-high-fb0c74c9.tar.gz

Additional context: initially I did not plan to attach diagnostics because this did not look like a crash/runtime bug. I viewed it as a structural design/semantics issue: the system executes normally, but prompt-role composition can still produce intent-priority problems. I am attaching zg report and trace bundle now because maintainers explicitly requested them for triage.

<!-- gh-comment-id:3944108019 --> @DarKWinGTM commented on GitHub (Feb 23, 2026): [zg-trace-2026-02-23_06-16-15_gemini-3.1-pro-high-fb0c74c9.tar.gz](https://github.com/user-attachments/files/25482512/zg-trace-2026-02-23_06-16-15_gemini-3.1-pro-high-fb0c74c9.tar.gz) Additional context: initially I did not plan to attach diagnostics because this did not look like a crash/runtime bug. I viewed it as a structural design/semantics issue: the system executes normally, but prompt-role composition can still produce intent-priority problems. I am attaching zg report and trace bundle now because maintainers explicitly requested them for triage.
Author
Owner

@NikkeTryHard commented on GitHub (Feb 23, 2026):

Implemented and validated in .

What changed:

  • Request assembly now keeps turn boundaries explicit ( / labels) in messages/completions/responses extraction paths.
  • Follow-up requests keep prior turn context instead of flattening intent into a single ambiguous blob.
  • Added regression tests for role-boundary preservation across affected API paths.

Verification:

  • cargo test --bin zerogravity: 267 passed

  • cargo build: passed

  • Targeted E2E checks for , , and all correctly answered the multi-turn name recall scenario ().

  • Full smoke suite (zg smoke — comprehensive smoke tests

    ── Standalone CLI ──

    1. Version .................................. PASS (v1.3.4) 0ms
    2. Usage text ............................... PASS 0ms
    3. Status ................................... PASS (ok) 207ms
    4. Accounts ................................. PASS (3 found) 0ms
    5. Account file ............................. PASS (readable & valid) 0ms

    ── API Endpoints ──
    6. Health ................................... PASS 4ms
    7. Models ................................... PASS (7 models) 4ms
    8. Chat Completions (sync) .................. PASS 4887ms
    9. Chat Completions (stream) ................ PASS 11810ms
    10. Responses API (sync) ..................... PASS 9343ms
    11. Messages API (sync) ...................... PASS 7202ms
    12. Multi-turn context ....................... PASS (name recalled) 5218ms
    13. System instruction ....................... PASS 17860ms
    14. Tool calling ............................. PASS (tool_calls returned) 27248ms
    15. Gemini native API ........................ PASS 2047ms
    16. Accounts API ............................. PASS 5ms
    17. Error handling ........................... PASS (proper error response) 5ms
    18. Usage endpoint ........................... PASS 4ms
    19. Quota endpoint ........................... PASS 4ms

    ── Docker Internal (SKIPPED — not in container) ──

    ✓ 19/19 passed (69.4s)): 19/19 passed after smoke runner rate-limit hardening.

Closing as fixed.

<!-- gh-comment-id:3944419021 --> @NikkeTryHard commented on GitHub (Feb 23, 2026): Implemented and validated in . What changed: - Request assembly now keeps turn boundaries explicit ( / labels) in messages/completions/responses extraction paths. - Follow-up requests keep prior turn context instead of flattening intent into a single ambiguous blob. - Added regression tests for role-boundary preservation across affected API paths. Verification: - cargo test --bin zerogravity: 267 passed - cargo build: passed - Targeted E2E checks for , , and all correctly answered the multi-turn name recall scenario (). - Full smoke suite (zg smoke — comprehensive smoke tests ── Standalone CLI ── 1. Version .................................. PASS (v1.3.4) 0ms 2. Usage text ............................... PASS 0ms 3. Status ................................... PASS (ok) 207ms 4. Accounts ................................. PASS (3 found) 0ms 5. Account file ............................. PASS (readable & valid) 0ms ── API Endpoints ── 6. Health ................................... PASS 4ms 7. Models ................................... PASS (7 models) 4ms 8. Chat Completions (sync) .................. PASS 4887ms 9. Chat Completions (stream) ................ PASS 11810ms 10. Responses API (sync) ..................... PASS 9343ms 11. Messages API (sync) ...................... PASS 7202ms 12. Multi-turn context ....................... PASS (name recalled) 5218ms 13. System instruction ....................... PASS 17860ms 14. Tool calling ............................. PASS (tool_calls returned) 27248ms 15. Gemini native API ........................ PASS 2047ms 16. Accounts API ............................. PASS 5ms 17. Error handling ........................... PASS (proper error response) 5ms 18. Usage endpoint ........................... PASS 4ms 19. Quota endpoint ........................... PASS 4ms ── Docker Internal (SKIPPED — not in container) ── ✓ 19/19 passed (69.4s)): 19/19 passed after smoke runner rate-limit hardening. Closing as fixed.
Author
Owner

@NikkeTryHard commented on GitHub (Feb 23, 2026):

Closing as fixed and verified.

<!-- gh-comment-id:3944419090 --> @NikkeTryHard commented on GitHub (Feb 23, 2026): Closing as fixed and verified.
Author
Owner

@NikkeTryHard commented on GitHub (Feb 23, 2026):

Correction note (previous comment formatting was mangled by shell escaping).

Fix summary:

  • Request assembly now keeps user and assistant turn boundaries explicit in messages/completions/responses extraction paths.
  • Follow-up requests now retain prior context instead of flattening into one ambiguous prompt.
  • Added regression tests for role-boundary preservation.

Verification summary:

  • cargo test --bin zerogravity: 267 passed
  • cargo build: passed
  • Targeted E2E checks on /v1/messages, /v1/chat/completions, and /v1/responses all correctly return Alice for the multi-turn recall scenario
  • Full smoke run after smoke hardening: 19/19 passed
<!-- gh-comment-id:3944420833 --> @NikkeTryHard commented on GitHub (Feb 23, 2026): Correction note (previous comment formatting was mangled by shell escaping). Fix summary: - Request assembly now keeps user and assistant turn boundaries explicit in messages/completions/responses extraction paths. - Follow-up requests now retain prior context instead of flattening into one ambiguous prompt. - Added regression tests for role-boundary preservation. Verification summary: - cargo test --bin zerogravity: 267 passed - cargo build: passed - Targeted E2E checks on /v1/messages, /v1/chat/completions, and /v1/responses all correctly return Alice for the multi-turn recall scenario - Full smoke run after smoke hardening: 19/19 passed
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
starred/zerogravity#50
No description provided.