mirror of
https://github.com/deepseek-ai/deepseek-harness.git
synced 2026-08-29 04:26:38 +00:00
fix(code-runtime-python): make the wire-field binding exhaustive over interface keys
The array-based FrameFields<T> only checked that listed names were members of the frame's keys, so a field added to a TS interface (e.g. LogMessage.seq?) left the existing arrays a valid subset — typecheck passed, and since the constant and Python both lacked the field the mirror test passed too. The JSDoc's claim that runtime covered this was false. Replace it with WIRE_FRAME_FIELD_ROLES, a per-frame map keyed by field name (`Record<RequiredKeys<T>, 'required'> & Record<OptionalKeys<T>, 'optional'>`), so every interface key MUST appear with a matching required/optional tag: an added field, a removed field, a rename, or an optionality flip all fail typecheck at the roles map (verified). WIRE_FRAME_FIELDS is projected from it as the sorted arrays the mirror test still compares to the Python TypedDicts. Also drop the `export` added to the promoted frame interfaces (Namespace, ErrorClass, RunMessage, DoneErrorField, ReplyOk, ReplyErr) — nothing outside protocol.ts imports them, so the barrel surface is unchanged and knip stays clean.
This commit is contained in:
@@ -16,7 +16,7 @@
|
||||
* the program-visible name the namespace is materialized under; `errorClass`,
|
||||
* when present, asks the bootstrap to mint a program-visible exception class.
|
||||
*/
|
||||
export interface Namespace {
|
||||
interface Namespace {
|
||||
global: string
|
||||
names: string[]
|
||||
errorClass?: ErrorClass
|
||||
@@ -26,7 +26,7 @@ export interface Namespace {
|
||||
* A namespace's program-visible exception class: rejected calls raise its
|
||||
* instances carrying the failed member name on `memberNameProperty`.
|
||||
*/
|
||||
export interface ErrorClass {
|
||||
interface ErrorClass {
|
||||
name: string
|
||||
memberNameProperty: string
|
||||
}
|
||||
@@ -55,7 +55,7 @@ export interface BootMessage {
|
||||
}
|
||||
|
||||
/** Host → Python: sent after `boot-ack`; carries only the model's program body. */
|
||||
export interface RunMessage {
|
||||
interface RunMessage {
|
||||
type: 'run'
|
||||
program: string
|
||||
}
|
||||
@@ -99,7 +99,7 @@ interface LogMessage {
|
||||
}
|
||||
|
||||
/** The failure carried on a {@link DoneMessage}: one of three kinds plus text. */
|
||||
export interface DoneErrorField {
|
||||
interface DoneErrorField {
|
||||
kind: 'exception' | 'invalid-output' | 'output-limit'
|
||||
message: string
|
||||
}
|
||||
@@ -129,7 +129,7 @@ interface DoneMessage {
|
||||
export type ChildToHost = BootAckMessage | CallMessage | LogMessage | DoneMessage
|
||||
|
||||
/** Host → Python: successful answer to one {@link CallMessage}. */
|
||||
export interface ReplyOk {
|
||||
interface ReplyOk {
|
||||
type: 'reply'
|
||||
id: number
|
||||
ok: true
|
||||
@@ -137,7 +137,7 @@ export interface ReplyOk {
|
||||
}
|
||||
|
||||
/** Host → Python: failed answer to one {@link CallMessage}. */
|
||||
export interface ReplyErr {
|
||||
interface ReplyErr {
|
||||
type: 'reply'
|
||||
id: number
|
||||
ok: false
|
||||
@@ -153,58 +153,70 @@ type RequiredKeys<T> = { [K in keyof T]-?: object extends Pick<T, K> ? never : K
|
||||
type OptionalKeys<T> = { [K in keyof T]-?: object extends Pick<T, K> ? K : never }[keyof T] & string
|
||||
|
||||
/**
|
||||
* Shape of one {@link WIRE_FRAME_FIELDS} entry, derived from frame interface
|
||||
* `T`. Every element of `required` must be one of `T`'s required keys and every
|
||||
* element of `optional` one of `T`'s optional keys — so a renamed field, or an
|
||||
* optionality flip (`truncated?` → `truncated`, which moves the name between the
|
||||
* two arrays' element types), fails typecheck. Completeness in the other
|
||||
* direction (every declared key actually appears, and no frame exists on only
|
||||
* one side of the wire) is enforced at runtime by the mirror test, which
|
||||
* compares these arrays to the Python `TypedDict`'s
|
||||
* `__required_keys__`/`__optional_keys__` by exact set equality over the full
|
||||
* frame roster.
|
||||
* Whether each key of frame `T` is a `'required'` or `'optional'` wire field.
|
||||
* Because it is `Record<keyof T, …>`, an entry MUST list every key — a field
|
||||
* added to the interface without a corresponding entry fails typecheck — and
|
||||
* `keyof T`-typed keys reject a name no frame declares. The `'required'` /
|
||||
* `'optional'` tag must match the field's actual optionality (checked by
|
||||
* {@link WIRE_FRAME_FIELDS}'s per-entry assertions), so an optionality flip is
|
||||
* caught too. This is the exhaustive counterpart the array form could not
|
||||
* express (a subset array satisfied it silently).
|
||||
*/
|
||||
type FrameFields<T> = {
|
||||
required: readonly RequiredKeys<T>[]
|
||||
optional: readonly OptionalKeys<T>[]
|
||||
type FrameFieldRoles<T> = Record<RequiredKeys<T>, 'required'> & Record<OptionalKeys<T>, 'optional'>
|
||||
|
||||
/**
|
||||
* Each frame's wire fields tagged by required/optional, keyed by field name so
|
||||
* the mapping is exhaustive over the frame interface (see
|
||||
* {@link FrameFieldRoles}). Bound to the interfaces by `satisfies` below, this
|
||||
* is the single source of truth the cross-language mirror test derives its
|
||||
* expectations from; {@link WIRE_FRAME_FIELDS} projects it to sorted
|
||||
* required/optional arrays for the comparison. `global` is the JSON key
|
||||
* {@link CallMessage} and {@link Namespace} send (a reserved word the Python
|
||||
* side carries via a functional `TypedDict`).
|
||||
*/
|
||||
const WIRE_FRAME_FIELD_ROLES = {
|
||||
BootMessage: { type: 'required', cpuSeconds: 'required', addressSpaceBytes: 'required', maxLogBytes: 'required', maxValueBytes: 'required', namespaces: 'required' },
|
||||
Namespace: { global: 'required', names: 'required', errorClass: 'optional' },
|
||||
RunMessage: { type: 'required', program: 'required' },
|
||||
BootAckMessage: { type: 'required' },
|
||||
CallMessage: { type: 'required', id: 'required', global: 'required', name: 'required', args: 'required' },
|
||||
LogMessage: { type: 'required', text: 'required', truncated: 'optional' },
|
||||
DoneErrorField: { kind: 'required', message: 'required' },
|
||||
DoneMessage: { type: 'required', value: 'optional', error: 'optional' },
|
||||
ErrorClass: { name: 'required', memberNameProperty: 'required' },
|
||||
ReplyOk: { type: 'required', id: 'required', ok: 'required', value: 'required' },
|
||||
ReplyErr: { type: 'required', id: 'required', ok: 'required', message: 'required' },
|
||||
} as const satisfies {
|
||||
BootMessage: FrameFieldRoles<BootMessage>
|
||||
Namespace: FrameFieldRoles<Namespace>
|
||||
RunMessage: FrameFieldRoles<RunMessage>
|
||||
BootAckMessage: FrameFieldRoles<BootAckMessage>
|
||||
CallMessage: FrameFieldRoles<CallMessage>
|
||||
LogMessage: FrameFieldRoles<LogMessage>
|
||||
DoneErrorField: FrameFieldRoles<DoneErrorField>
|
||||
DoneMessage: FrameFieldRoles<DoneMessage>
|
||||
ErrorClass: FrameFieldRoles<ErrorClass>
|
||||
ReplyOk: FrameFieldRoles<ReplyOk>
|
||||
ReplyErr: FrameFieldRoles<ReplyErr>
|
||||
}
|
||||
|
||||
/**
|
||||
* The wire field names of each frame, split into required and optional keys, as
|
||||
* a RUNTIME value the cross-language mirror test asserts `py/protocol.py`'s
|
||||
* `TypedDict`s against. The `satisfies` clause binds each entry to its frame
|
||||
* interface via {@link FrameFields}, which derives the required/optional key
|
||||
* sets FROM the interface — so a renamed, removed, or optionality-flipped field
|
||||
* on the TS side fails typecheck, and the mirror test catches a Python-side
|
||||
* divergence at runtime. `global` is the JSON key {@link CallMessage} and
|
||||
* {@link Namespace} send (a reserved word the Python side carries via a
|
||||
* functional `TypedDict`).
|
||||
* The wire field names of each frame, split into sorted required and optional
|
||||
* key arrays — the shape the cross-language mirror test compares against
|
||||
* `py/protocol.py`'s `TypedDict` `__required_keys__`/`__optional_keys__`.
|
||||
* Projected from {@link WIRE_FRAME_FIELD_ROLES}, so it inherits that mapping's
|
||||
* exhaustive, optionality-checked binding to the frame interfaces: a TS-side
|
||||
* field add, remove, rename, or optionality flip fails typecheck at the roles
|
||||
* map, and a Python-side divergence fails the mirror test at runtime.
|
||||
*/
|
||||
export const WIRE_FRAME_FIELDS = {
|
||||
BootMessage: { required: ['addressSpaceBytes', 'cpuSeconds', 'maxLogBytes', 'maxValueBytes', 'namespaces', 'type'], optional: [] },
|
||||
Namespace: { required: ['global', 'names'], optional: ['errorClass'] },
|
||||
RunMessage: { required: ['program', 'type'], optional: [] },
|
||||
BootAckMessage: { required: ['type'], optional: [] },
|
||||
CallMessage: { required: ['args', 'global', 'id', 'name', 'type'], optional: [] },
|
||||
LogMessage: { required: ['text', 'type'], optional: ['truncated'] },
|
||||
DoneErrorField: { required: ['kind', 'message'], optional: [] },
|
||||
DoneMessage: { required: ['type'], optional: ['error', 'value'] },
|
||||
ErrorClass: { required: ['memberNameProperty', 'name'], optional: [] },
|
||||
ReplyOk: { required: ['id', 'ok', 'type', 'value'], optional: [] },
|
||||
ReplyErr: { required: ['id', 'message', 'ok', 'type'], optional: [] },
|
||||
} satisfies {
|
||||
BootMessage: FrameFields<BootMessage>
|
||||
Namespace: FrameFields<Namespace>
|
||||
RunMessage: FrameFields<RunMessage>
|
||||
BootAckMessage: FrameFields<BootAckMessage>
|
||||
CallMessage: FrameFields<CallMessage>
|
||||
LogMessage: FrameFields<LogMessage>
|
||||
DoneErrorField: FrameFields<DoneErrorField>
|
||||
DoneMessage: FrameFields<DoneMessage>
|
||||
ErrorClass: FrameFields<ErrorClass>
|
||||
ReplyOk: FrameFields<ReplyOk>
|
||||
ReplyErr: FrameFields<ReplyErr>
|
||||
}
|
||||
export const WIRE_FRAME_FIELDS: Record<keyof typeof WIRE_FRAME_FIELD_ROLES, { required: string[]; optional: string[] }> =
|
||||
Object.fromEntries(
|
||||
Object.entries(WIRE_FRAME_FIELD_ROLES).map(([frame, roles]) => {
|
||||
const required = Object.keys(roles).filter(key => (roles as Record<string, string>)[key] === 'required').sort()
|
||||
const optional = Object.keys(roles).filter(key => (roles as Record<string, string>)[key] === 'optional').sort()
|
||||
return [frame, { required, optional }]
|
||||
}),
|
||||
) as Record<keyof typeof WIRE_FRAME_FIELD_ROLES, { required: string[]; optional: string[] }>
|
||||
|
||||
|
||||
/**
|
||||
|
||||
Reference in New Issue
Block a user