Просмотр исходного кода

Merge pull request #115 from deepseek-harness/worktree-ci-lint-heap

ci: fix lint OOM and surface.ts coverage gap (re-enabled CI)
Tianyi Cui 2 месяцев назад
Родитель
Сommit
185c09d6a5
2 измененных файлов с 44 добавлено и 1 удалено
  1. 5 0
      .github/workflows/ci.yml
  2. 39 1
      packages/core/session/tests/surface.spec.ts

+ 5 - 0
.github/workflows/ci.yml

@@ -39,8 +39,13 @@ jobs:
       - name: Typecheck (src + tests + examples)
         run: pnpm run typecheck
 
+      # Type-aware ESLint loads every package tsconfig through the project
+      # service and peaks at ~3.4GB; the default V8 old-space ceiling (~2GB)
+      # OOMs it (exit 134). Raise the ceiling well above the peak.
       - name: Lint
         run: pnpm run lint
+        env:
+          NODE_OPTIONS: --max-old-space-size=8192
 
       # Doc-sync gates (doc-sync-enforcement RFC). doc-typecheck compiles the
       # fenced ts blocks against the root project-reference graph. The cordis

+ 39 - 1
packages/core/session/tests/surface.spec.ts

@@ -1,6 +1,6 @@
 import { describe, expect, it } from 'vitest'
 import type { SessionEvent, SurfaceEvent, SurfaceEventType } from '@deepseek-ai/dsh-session'
-import { Session, SessionId } from '@deepseek-ai/dsh-session'
+import { Session, SessionId, isSurfaceEligibleType, isSurfaceEvent } from '@deepseek-ai/dsh-session'
 import { CallId } from '@deepseek-ai/dsh-llm'
 
 /** Build a minimal session with turn boundaries and a single user message. */
@@ -279,3 +279,41 @@ describe('Session.append surface opts', () => {
     expect(event.surfaceOp).toBe('append')
   })
 })
+
+describe('surface type guards', () => {
+  it('isSurfaceEligibleType is true only for message-producing types', () => {
+    expect(isSurfaceEligibleType('user/message')).toBe(true)
+    expect(isSurfaceEligibleType('assistant/message')).toBe(true)
+    expect(isSurfaceEligibleType('tool/result')).toBe(true)
+    expect(isSurfaceEligibleType('context/message')).toBe(true)
+    expect(isSurfaceEligibleType('steering/message')).toBe(true)
+    expect(isSurfaceEligibleType('turn/start')).toBe(false)
+    expect(isSurfaceEligibleType('assistant/chunk')).toBe(false)
+  })
+
+  it('isSurfaceEvent narrows a fully-formed surface event', () => {
+    const s = surfaceSession()
+    const userMessage = s.events.find(e => e.type === 'user/message')!
+    expect(isSurfaceEvent(userMessage)).toBe(true)
+  })
+
+  it('isSurfaceEvent rejects a non-surface-eligible type', () => {
+    const s = surfaceSession()
+    const turnStart = s.events.find(e => e.type === 'turn/start')!
+    expect(isSurfaceEvent(turnStart)).toBe(false)
+  })
+
+  it('isSurfaceEvent rejects a surface-eligible type missing its surfaceOp marker', () => {
+    // A surface-eligible type whose mandatory surfaceOp is absent — the state a
+    // seed/load log can carry before the marker is validated. surfaceOp is
+    // optional on SessionEvent, so this is a representable runtime value.
+    const markerless: SessionEvent = {
+      type: 'user/message',
+      seq: 0,
+      time: 0,
+      data: { content: [{ type: 'text', text: 'hi' }], source: { kind: 'user' } },
+    }
+    expect(isSurfaceEligibleType(markerless.type)).toBe(true)
+    expect(isSurfaceEvent(markerless)).toBe(false)
+  })
+})