Browse Source

fix(web-tests): generate native system head before chat users

The long-chat builder emitted current V3 with user/message before system/message, so seed restoration rejected its protected head before browser assertions ran. Open the first step and append the system head before the user surface; keep Session-assigned title and tool-result references.

Add an independent seed-reader regression for current version, protected head order, title/tool references and closed turns. Observed protected-head failure before the fix; focused regression and full configured type-aware lint pass. Existing browser scroll assertions are unchanged; full built owner runs follow.
Tianyi Cui 1 month ago
parent
commit
dda0f05999
2 changed files with 27 additions and 2 deletions
  1. 24 0
      apps/web/tests/chat-scroll-contract.e2e.ts
  2. 3 2
      apps/web/tests/chat-scroll-fixture.ts

+ 24 - 0
apps/web/tests/chat-scroll-contract.e2e.ts

@@ -16,6 +16,7 @@ import type { SessionEvent } from '@deepseek-ai/dsh-session'
 import { createChatScrollFixture, type ChatScrollFixture } from './chat-scroll-fixture.ts'
 import {
   launchWebScaffold,
+  parseSeedFixture,
   seedSession,
   watchConsole,
   webSnapshotMode,
@@ -471,6 +472,29 @@ function assertClean(world: ScrollWorld): void {
   expect(world.tripwire.warnings).toEqual([])
 }
 
+it('generates a native V3 scroll seed with a protected system head and intact references', () => {
+  const { header, events } = parseSeedFixture(HISTORY_FIXTURE.log)
+  expect(header.version).toBe(3)
+  expect(events.slice(0, 5).map(event => event.type)).toEqual([
+    'turn/start', 'step/start', 'system/message', 'user/message', 'session/title',
+  ])
+  expect(events.filter(event => event.type === 'system/message')).toHaveLength(1)
+  const firstUser = events.find(event => event.type === 'user/message')!
+  const title = events.find(event => event.type === 'session/title')!
+  expect(title.data.messageSeqs).toEqual([firstUser.seq])
+  const calls = events.filter(event => event.type === 'tool/call')
+  const results = events.filter(event => event.type === 'tool/result')
+  expect(calls).toHaveLength(22)
+  expect(results).toHaveLength(calls.length)
+  for (const result of results) {
+    const call = calls.find(event => event.data.callId === result.data.message.source.callId)!
+    expect(result.sourceEventSeqs).toEqual([call.seq])
+    expect(call.seq).toBeLessThan(result.seq)
+  }
+  expect(events.filter(event => event.type === 'turn/end')).toHaveLength(HISTORY_FIXTURE.turns)
+  expect(events.at(-1)?.type).toBe('turn/end')
+})
+
 let browser: Browser
 
 describe('web e2e: long Chat scroll contract', () => {

+ 3 - 2
apps/web/tests/chat-scroll-fixture.ts

@@ -199,6 +199,9 @@ export function createChatScrollFixture(options: ChatScrollFixtureOptions): Chat
     session.append('turn/start', {
       turn,
     })
+    session.append('step/start', { turn, step: 1 })
+    // Native V3 installs the protected system head before any user surface.
+    if (turn === 1) appendSystemPrompt(session, turn, 1)
     const user = session.append('user/message', createUserMessage({
       content: text(
         `${markers.user(turn)} Review the long-running conversation state for turn ${String(turn)}. `
@@ -214,8 +217,6 @@ export function createChatScrollFixture(options: ChatScrollFixtureOptions): Chat
       })
     }
 
-    session.append('step/start', { turn, step: 1 })
-    if (turn === 1) appendSystemPrompt(session, turn, 1)
     appendRequestHeader(session, turn, 1)
     if (turn % TOOL_INTERVAL === 0) {
       appendToolStep(session, markers, turn)