fix(invariants): assert runtime relationships, not API shapes

This commit is contained in:
Tianyi Cui
2026-07-20 19:34:19 +08:00
parent 1254c07025
commit 1145ee5fc3
124 changed files with 2923 additions and 2334 deletions

View File

@@ -1,30 +1,64 @@
/** Package-owned runtime contract checks for `@deepseek-ai/dsh-time-context`. @module @deepseek-ai/dsh-time-context/invariant */
/** Package-owned durable clock-context invariants. @module @deepseek-ai/dsh-time-context/invariant */
import type { Context } from 'cordis'
import { observePluginInvariant, type InvariantInstaller } from '@deepseek-ai/dsh-invariants'
import type { Session, SessionEvent } from '@deepseek-ai/dsh-session'
import type { InvariantFailure, InvariantInstaller } from '@deepseek-ai/dsh-invariants'
const PACKAGE_NAME = '@deepseek-ai/dsh-time-context'
const SOURCE_NAME = 'time-context'
const READING = new RegExp(
'^Time sampled while preparing turn (\\d+), step (\\d+): '
+ '(\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}:\\d{2}(?:Z|[+-]\\d{2}:\\d{2})\\[[^\\]]+\\])\\n'
+ 'Elapsed since the preceding (model-visible message|step context): '
+ '(?:unavailable|(?:(?:\\d+d )?(?:\\d+h )?(?:\\d+m )?\\d+s))\\.$',
)
/** Cordis companion plugin name. */
export const name = 'time-context-invariant'
/** Services required before the companion can register. */
/** Service required before the companion can reserve package ownership. */
export const inject = ['invariants']
/** Install checks for this package's active plugin fibers. */
/** Validate one plugin-attributed time reading against its durable event timestamp. */
function validateReading(event: SessionEvent<'context/message'>, fail: InvariantFailure): void {
const [block] = event.data.content
if (event.data.content.length !== 1 || block?.type !== 'text') {
fail('time-context messages must contain exactly one text block')
}
const match = READING.exec(block.text)
if (match === null) fail('time-context message does not match the durable reading format')
const turn = Number(match[1])
const step = Number(match[2])
if (!Number.isSafeInteger(turn) || turn < 1 || !Number.isSafeInteger(step) || step < 1) {
fail('time-context turn and step must be positive safe integers')
}
const baseline = match[4]
if ((step === 1) !== (baseline === 'model-visible message')) {
fail(`time-context step ${step} uses the wrong elapsed-time baseline ${JSON.stringify(baseline)}`)
}
const rendered = match[3]
/* v8 ignore next -- the preceding fixed regexp always supplies capture group three. */
if (rendered === undefined) fail('time-context reading omitted its rendered timestamp')
const renderedTime = Date.parse(rendered.replace(/\[[^\]]+\]$/, ''))
if (!Number.isFinite(renderedTime) || !Number.isSafeInteger(event.time)
|| event.time < renderedTime || event.time - renderedTime >= 1_000) {
fail('time-context rendered timestamp must identify the durable event second')
}
}
/** Install validation for plugin-attributed context readings. */
const install: InvariantInstaller = (ctx, fail) => {
observePluginInvariant(ctx, fail, {
name: 'time-context',
inject: [
'agents',
],
effects: [
'ctx.on("agent/pre-step")',
],
})
ctx.on('internal/dispatch', (_mode, eventName, args) => {
if (eventName !== 'session/event') return
const event = (args as [Session, SessionEvent])[1]
if (event.type !== 'context/message'
|| event.data.source.kind !== 'plugin'
|| event.data.source.plugin !== SOURCE_NAME) return
validateReading(event, fail)
}, { global: true })
}
/**
* Register this package's invariant companion.
* Register the time-context invariant companion.
* @param ctx - Cordis context carrying the invariant service.
* @returns the installed registration's disposer after setup succeeds.
*/

View File

@@ -0,0 +1,83 @@
import { describe, expect, it } from 'vitest'
import { Context } from 'cordis'
import type { ContentBlock } from '@deepseek-ai/dsh-llm'
import type { Session, SessionEvent } from '@deepseek-ai/dsh-session'
import * as TimeInvariant from '@deepseek-ai/dsh-time-context/invariant'
import InvariantService from '@deepseek-ai/dsh-invariants'
const SECOND = Date.parse('2026-07-14T00:00:00Z')
async function setup(): Promise<Context> {
const ctx = new Context()
await ctx.plugin(InvariantService)
await ctx.plugin(TimeInvariant)
return ctx
}
function event(text: string, time = SECOND + 456, content?: unknown[]): SessionEvent {
return {
type: 'context/message',
seq: 0,
time,
data: {
content: (content ?? [{ type: 'text', text }]) as ContentBlock[],
source: { kind: 'plugin', plugin: 'time-context' },
},
}
}
function reading(
turn = '1',
step = '1',
baseline = 'model-visible message',
timestamp = '2026-07-14T00:00:00+00:00[UTC]',
): string {
return `Time sampled while preparing turn ${turn}, step ${step}: ${timestamp}\n`
+ `Elapsed since the preceding ${baseline}: unavailable.`
}
describe('time-context invariants', () => {
it('accepts a reading whose turn, step, baseline, and timestamp agree', async () => {
const ctx = await setup()
const text = 'Time sampled while preparing turn 2, step 3: 2026-07-14T00:00:00+00:00[UTC]\n'
+ 'Elapsed since the preceding step context: 4m 2s.'
expect(() => { ctx.emit('session/event', {} as Session, event(text)) }).not.toThrow()
})
it.each([
['not a reading', SECOND, undefined, /durable reading format/],
[reading('0'), SECOND, undefined, /positive safe integers/],
[reading('999999999999999999999'), SECOND, undefined, /positive safe integers/],
[reading('1', '0', 'step context'), SECOND, undefined, /positive safe integers/],
[reading('1', '999999999999999999999', 'step context'), SECOND, undefined, /positive safe integers/],
[reading('1', '1', 'step context'), SECOND, undefined, /wrong elapsed-time baseline/],
[reading('1', '2', 'model-visible message'), SECOND, undefined, /wrong elapsed-time baseline/],
[reading('1', '1', 'model-visible message', '2026-99-99T00:00:00+00:00[UTC]'), SECOND, undefined, /durable event second/],
[reading(), Number.NaN, undefined, /durable event second/],
[reading(), SECOND - 1, undefined, /durable event second/],
[reading(), SECOND + 1_000, undefined, /durable event second/],
['ignored', SECOND, [], /exactly one text block/],
['ignored', SECOND, [{ type: 'image', data: 'x', mimeType: 'image/png' }], /exactly one text block/],
['ignored', SECOND, [{ type: 'text', text: 'one' }, { type: 'text', text: 'two' }], /exactly one text block/],
] as const)('rejects an incoherent durable reading', async (text, time, content, message) => {
const ctx = await setup()
expect(() => {
ctx.emit('session/event', {} as Session, event(text, time, content === undefined ? undefined : [...content]))
}).toThrow(message)
})
it('ignores context messages owned by another package', async () => {
const ctx = await setup()
const other = event('unrelated') as SessionEvent<'context/message'>
other.data.source = { kind: 'plugin', plugin: 'other' }
expect(() => { ctx.emit('session/event', {} as Session, other) }).not.toThrow()
other.data.source = { kind: 'user' }
expect(() => { ctx.emit('session/event', {} as Session, other) }).not.toThrow()
expect(() => {
ctx.emit('session/event', {} as Session, {
type: 'turn/start', seq: 0, time: 0, data: { turn: 1, trigger: { kind: 'message', source: { kind: 'user' } } },
})
ctx.emit('tools/change')
}).not.toThrow()
})
})