Skip to content

fix: hoist leading system/developer prefix out of the openai fold space - #120

Merged
ranxianglei merged 1 commit into
masterfrom
2026-08-22_openai-system-hoist
Aug 22, 2026
Merged

fix: hoist leading system/developer prefix out of the openai fold space#120
ranxianglei merged 1 commit into
masterfrom
2026-08-22_openai-system-hoist

Conversation

@ranxianglei

Copy link
Copy Markdown
Owner

fix: hoist leading system/developer prefix out of the openai fold space

Symptom (restart regression, omp @ glm-5.3)

  • Panel pre-restart: 3 active blocks (383k compressed), sent 146k.
  • Panel post-restart: Blocks: none, sent balloons to 546k, all ranges reappear uncompressed.
  • /home/dog/.omp/acp-omp.log on restart: prime-fold … blocks=0 + fold-replay rejected reason=fp m00001..m00264 want 1d8b6f39 got 432bb7b2 @0..264 — positions resolve, fingerprint mismatches; 20 s later the first live request replays the same calls successfully (event=replayed).

Root cause

The openai codec was the only one folding the system prompt into the message-id space:

codec system location
anthropic top-level system field (out of fold space, issue #64)
responses instructions + systemParts, never in msgs
openai messages[0] → fold piece at pos 0 (inside)

Two failure modes follow:

  1. Compression eats the system prompt. Dump sweep (~/.omp/acp-omp-dumps/req_76*.json, sid 01a027f3): pre-compress requests carry systemLen=45151 (824–828 msgs); the moment compression covered m00001..m00264 (which includes the system piece) the wire dropped to systemLen=0 on every subsequent request. The model received no system prompt at all after the first compression.
  2. Restart replay rejected. primeFold reconstructs system content from host state (runtime injections differ across restarts: 45k live vs ~7.4k reconstructed). Since the system piece sat at pos 0 inside the id space, every span fingerprint covering it diverged, and the guard correctly rejected the replay → "Blocks: none" until the first live request re-derived ids in live space.

Fix

openaiToCore now returns { msgs, systemText }:

  • the contiguous leading system/developer prefix is hoisted out of the fold space (joined with \n\n);
  • mid-conversation system messages (rare, host-synthetic) stay in the fold space unchanged, keeping the originalRole round-trip;
  • conversation ids no longer depend on system content at all.

mirrorOpenaiToCore needs no change: the mirror emits systemText as messages[0], which now folds through the same hoist — mirror and live spaces converge regardless of systemText, which is exactly what restart replay needs.

Re-injection stays with the existing injectOpenaiSystem (callers add [originalSystem, compressPrompt]).

Tests

  • new tests/openai-system-hoist.test.ts (6): prefix hoisted + ids identical to system-free body; empty systemText; mid-conversation system kept; system-content changes never shift ids (restart regression); mirror converges with live for arbitrary systemText; re-injection round-trip.
  • tests/wire-bili-message-roundtrip.test.ts: the two tests encoding the old contract (leading-only system/developer bodies) rewritten — prefix hoist + mid-conversation originalRole round-trip.

Suite: 428/428, tsc --noEmit clean.

Downstream

  • omp: capture systemText at the fold and re-inject the original on rebuild (follow-up PR + e2e restart regression with live system ≠ reconstruction).
  • proxy: server.ts openai branch currently only injects the compress prompt via injectOpenaiSystem; after this change it must prepend the original systemText too (follow-up PR).
  • pi: does not use the openai codecs — zero impact.

…ld space

The openai codec was the last one folding the system prompt into the
message id space (anthropic keeps it in the top-level system field,
responses collects it as instructions). Two failure modes:

1. A compress range covering pos 0 removed the model's system prompt
   from the rebuilt wire entirely (observed: glm-5.3 wire systemLen
   45151 -> 0 after the first compression).
2. Restart replay: prime-fold reconstructs system content from host
   state (runtime injections differ across restarts), shifting every
   span fingerprint covering pos 0, so the guard rejected the replay
   and blocks showed as none until the first live request.

openaiToCore now returns { msgs, systemText } with the contiguous
leading system/developer prefix hoisted out; mid-conversation system
traffic stays in the fold space unchanged. mirrorOpenaiToCore needs no
change: the mirror system message folds through the same hoist, so
mirror and live spaces converge regardless of systemText.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant