Files
dsh-context/tests/host/fallback.spec.ts
T
bowenliang123 9acbf7eb45 feat: support dsh 0.1.5-rc.1+ and 0.1.7-rc.2+ as the supported lines
The supported range is now two baselines - dsh-v0.1.5-rc.1 (session
format V3) and dsh-v0.1.7-rc.2 (V4) - enforced by the runtime gate at
BASELINE_DSH_VERSION = 0.1.5-rc.1; below it the fallback units and the
upgrade modal take over (0.1.2/0.1.3 hosts were previously served by the
real folds).

The pre-V3 log shapes are dead code on every supported harness: the
running harness migrates historical logs on every read path (or refuses
the session wholesale), and 0.1.5-rc.1 rejects a request/header carrying
header.system outright. The fold therefore drops the V0/V2-only paths -
the header.system envelope system prompt, assistant/chunk floods,
{start, end} replacement endpoints, tool/code-dispatch - and the state
fields only they wrote (systemsFromHeader, the stepStart decode
accumulators). stateVersion stays 20: no supported log folds
differently, and pre-V3-era cached rows are discarded by the projection
cache's identity gate (and the strict stepStart sub-schema) instead of
served. The headers unit folds metadata-only epochs (envelope
systemTokens write dropped; the v1 cached-row read-compat and its pinned
stateVersion 1 stay); replaceRangeOf speaks startSeq/endSeq only; the
browser's epoch fetch maps request/header to tools and system/message to
the prompt text.

tests/baselines.ts re-fits to the two entries (the right Sidebar seam is
required on both), the compat matrix drives both tags' REAL sources
(canonical logs re-shaped to V3, the hostile probe renumbered past them),
and the version fixtures pin the new floor. Manifest: dshReleases
{0.1.5-rc.1, 0.1.7-rc.2}, dsh peers >=0.1.5-rc.1 (satisfies the 0.1.7
admission check under includePrerelease), devDeps pinned to the
0.1.5-rc.1 surface plus the ui-primitives runtime closure the client
lane inlines (test-only), and the release workflow fetches the two tags.
docs/compatibility.md is rewritten around the two-line matrix; README,
AGENTS.md and the issue template follow.

Also riding this move, from the audit pass: the fold re-proves an inject
source's form before it books the event record (the harness validates
`kind` but not `form` - a hostile one previously failed the strict
wire/state schemas on every delivery, the #44 freeze class), the headers
unit guards a data-less envelope (it has no try/catch of its own), the
schemastery schema annotations drop the explicit z<T> wrappers the 3.18.4
Mode generics reject, and the version-history note records why
stateVersion is deliberately not bumped.
2026-09-25 01:08:38 +08:00

108 lines
5.1 KiB
TypeScript

// The baseline gate's fallback units (src/host/fallback.ts), driven over the
// REAL session-projection registry: the gate's value arrives through the
// ordinary snapshot cut no matter what the log carries, both registry
// contract generations read their own shape off the definition, and cached
// rows from either direction seed harmlessly.
import assert from 'node:assert/strict'
import { describe, test } from 'vitest'
import { Context } from '@deepseek-ai/cordis'
import SessionStore from '@deepseek-ai/dsh-session'
import type { Session } from '@deepseek-ai/dsh-session'
import SessionProjectionRegistry from '@deepseek-ai/dsh-session-projection'
import { createFallbackActivityDefinition, createFallbackHeadersDefinition, createFallbackTimelineDefinition } from '../../src/host/fallback'
import { BASELINE_DSH_VERSION } from '../../src/shared/version'
const CURRENT = '0.1.1-rc.2'
async function boot() {
const ctx = new Context()
await ctx.plugin(SessionStore)
await ctx.plugin(SessionProjectionRegistry)
ctx.sessionProjections.register(createFallbackTimelineDefinition(CURRENT) as never)
ctx.sessionProjections.register(createFallbackHeadersDefinition() as never)
return ctx
}
function appendAnything(session: Session): void {
session.append('request/header', {
header: { config: { model: 'deepseek-v4-flash', provider: 'deepseek' } },
reason: 'initial',
})
session.append('user/message', {
content: [{ type: 'text', text: 'hi' }],
source: { kind: 'user' },
} as never, { surfaceOp: 'append' })
}
describe('fallback units over the real registry', () => {
test('the gate value arrives through the snapshot cut regardless of the log', async () => {
const ctx = await boot()
const session = ctx.sessions.create()
appendAnything(session)
const timeline = ctx.sessionProjections.snapshot(session).values.contextTimeline
assert.ok(timeline !== undefined)
assert.equal(timeline.ok, true)
assert.deepEqual(timeline.unsupported, { current: CURRENT, minimum: BASELINE_DSH_VERSION })
assert.equal(timeline.current.total, 0, 'nothing is folded')
assert.equal(timeline.requests.length, 0)
assert.equal(timeline.nodes.length, 0)
assert.equal(timeline.archive.length, 0)
assert.equal(timeline.droppedNodes, 0)
assert.equal(timeline.model, undefined)
const headers = ctx.sessionProjections.snapshot(session).values.contextHeaders
assert.ok(headers !== undefined)
assert.deepEqual(headers.headers, [])
})
test('the served views pass the real wire schemas', () => {
const timeline = createFallbackTimelineDefinition(CURRENT)
assert.equal(timeline.wire.viewSchema.safeParse(timeline.wire.view({})).success, true)
const headers = createFallbackHeadersDefinition()
assert.equal(headers.wire.viewSchema.safeParse(headers.wire.view({})).success, true)
})
test('apply is a total identity over any event', () => {
const timeline = createFallbackTimelineDefinition(CURRENT)
const state = timeline.init()
assert.deepEqual(state, {})
assert.ok(timeline.apply(state, { type: 'user/message', seq: 1, time: 0, data: {} } as never) === state)
assert.ok(timeline.apply(state, { type: 'bogus/event' } as never) === state, 'unknown event types pass through')
const headers = createFallbackHeadersDefinition()
assert.ok(headers.apply(state, { type: 'request/header', seq: 1, time: 0, data: null } as never) === state)
})
test('the state schema strips any cached row instead of rejecting it', () => {
const timeline = createFallbackTimelineDefinition(CURRENT)
// A ver-1 row of the REAL headers unit ({headers: [...]}) or any other
// persisted shape seeds the gate's fold stripped to the empty state.
assert.deepEqual(timeline.stateSchema.parse({ headers: [{ seq: 1 }] }), {})
assert.deepEqual(timeline.stateSchema.parse({ surface: [null], sums: {} }), {})
assert.equal(timeline.stateVersion, 1)
})
test('both registry contract generations read their shape off one definition', () => {
const timeline = createFallbackTimelineDefinition(CURRENT)
// Pre-0.1.1 aliases: top-level `schema` + `view`.
assert.equal(timeline.schema.safeParse(timeline.view({})).success, true)
assert.deepEqual(timeline.view({}).unsupported, { current: CURRENT, minimum: BASELINE_DSH_VERSION })
const headers = createFallbackHeadersDefinition()
assert.equal(headers.schema.safeParse(headers.view({})).success, true)
assert.deepEqual(headers.view({}), { headers: [] })
})
test('the activity gate serves an empty ledger through both contract generations', () => {
const activity = createFallbackActivityDefinition()
assert.equal(activity.key, 'contextActivity')
assert.equal(activity.stateVersion, 1)
assert.deepEqual(activity.init(), {})
const state = activity.init()
assert.ok(activity.apply(state, { type: 'assistant/message', seq: 1, time: 0, data: {} } as never) === state)
assert.equal(activity.wire.viewSchema.safeParse(activity.wire.view({})).success, true)
assert.equal(activity.schema.safeParse(activity.view({})).success, true)
assert.deepEqual(activity.view({}), { days: {} })
})
})