refactor(comments): rewrap test comments onto fuller lines

Merge consecutive short comment lines in tests/** up to the 140-col limit
without touching their text: comment-stripped content, comment text (whitespace
normalised) and line-comment runs are all proven identical per file. This is the
line-count half of the cleanup only; the content pass over tests/** is still
outstanding.
This commit is contained in:
bowenliang123 committed 2026-10-10 00:26:42 +08:00
1 parent 537e53a56a
commit 7c87ce2b76
90 files changed
+273 -577

No files matched your search

+28 -62
View File
@@ -26,13 +26,10 @@ export interface ClientSeam {
markdownChrome: string
/** The platform module table the shell seeds (client-bundle requires must resolve). */
platformModules: readonly string[]
/**
* The faces the split timeline's on-demand detail channel rides
/** The faces the split timeline's on-demand detail channel rides
* (host/detail.ts + client/timelineSource.ts): the host's generic
* Connection RPC registry, the browser caller, and the projection
* registry's `stateOf` read. Each probe is a needle in the named source
* file of the baseline tag.
*/
* registry's `stateOf` read. Each probe is a needle in the named source file of the baseline tag. */
detailChannel: {
/** `HostConnectionRpc` in the connection package's shared rpc source. */
hostRpcFile: string
@@ -44,15 +41,13 @@ export interface ClientSeam {
registryFile: string
registryNeedle: string
}
/**
* The session-jump seam (the Context Dashboard's session cards and the
/** The session-jump seam (the Context Dashboard's session cards and the
* Agent network card's nodes — issue #90). Every supported line selects a
* session through the view owner's `uiWorkspace.openSession` (the sidebar
* row click's own verb), re-spelled `openSession(target: SessionTarget)` by
* the 0.1.6 selection refactor, which also retired the sessions service's
* own `open(id)` spelling — absent on every supported line, probed so a
* resurrection names the seam instead of silently dead-ending the jump.
*/
* resurrection names the seam instead of silently dead-ending the jump. */
sessionNav: {
/** The view-owner navigation verb's source, plus this line's signature spelling. */
workspaceFile: string
@@ -61,14 +56,12 @@ export interface ClientSeam {
sessionsFile: string
sessionsOpen: boolean
}
/**
* The Context Insights page's first-level panel seam: the layout's keyed
/** The Context Insights page's first-level panel seam: the layout's keyed
* `main` slot (the page's seat — also the conversation panel's own) and
* the sidebar's `sidebar.panellist` list (the entry's seat), addressed by
* one shared id. Both spellings shipped with the sidebar/layout split and
* are present on every supported line; probed presence-as-declared so a
* rename names the seam instead of silently orphaning the page.
*/
* rename names the seam instead of silently orphaning the page. */
insightPage: {
/** The panellist contract's declaring source, plus the slot's spelling in it. */
panellistFile: string
@@ -77,12 +70,10 @@ export interface ClientSeam {
mainFile: string
mainNeedle: string
}
/**
* The right Sidebar's tab seam — every supported generation ships it. The
/** The right Sidebar's tab seam — every supported generation ships it. The
* plugin's registration stays an OPTIONAL deferred inject: a below-baseline
* host (the gate's fallback composition) may lack the service entirely, and
* the client must simply never register the tab there instead of pending.
*/
* the client must simply never register the tab there instead of pending. */
sidebar: {
/** The tab-type registry service's providing source. */
serviceFile: string
@@ -90,29 +81,22 @@ export interface ClientSeam {
/** The keyed body seat's declaring source. */
slotFile: string
slotNeedle: string
/**
* The keyed chip-title seat the plugin's tab type also registers into
/** The keyed chip-title seat the plugin's tab type also registers into
* (the emblem-beside-label idiom the shipped files type uses). Present
* wherever the body seat is; probed separately so a rename names the seam.
*/
* wherever the body seat is; probed separately so a rename names the seam. */
titleSlotNeedle: string
/**
* The navigation face the plugin's file-open affordance rides
/** The navigation face the plugin's file-open affordance rides
* (`ctx.sidebarRight.openResource`): the controller's providing source and
* the needle in it. Same package as the registry, so present wherever the
* tab seam is; probed so a rename names the preview seam instead of
* silently degrading the affordance.
*/
* tab seam is; probed so a rename names the preview seam instead of silently degrading the affordance. */
nav: {
/** The controller source (the public navigation face's implementation). */
file: string
/** The `openResource` verb's declaration/comments marker. */
needle: string
}
/**
* The guide-entry contract the plugin's contribution must satisfy: one
* needle per field `SidebarRightGuideEntry` carries on this generation.
*/
/** The guide-entry contract the plugin's contribution must satisfy: one
* needle per field `SidebarRightGuideEntry` carries on this generation. */
guideEntry: {
/** The declaring source. */
file: string
@@ -128,42 +112,31 @@ export interface Baseline {
tag: string
/** The vendored @deepseek-ai/cordis release that harness line ships. */
cordis: string
/**
* The @deepseek-ai/dsh-session release that line vendors. The staged
/** The @deepseek-ai/dsh-session release that line vendors. The staged
* session-projection sources import runtime values from it (SessionLogOffset /
* SessionSeq), so the compat driver must resolve the specifier to the tag's
* own generation.
*/
* SessionSeq), so the compat driver must resolve the specifier to the tag's own generation. */
session: string
/**
* The durable-event families THIS line's log carries that the host fold
/** The durable-event families THIS line's log carries that the host fold
* switches on — the probe asserts every one exists in the tag's
* `KNOWN_SESSION_EVENT_TYPES`. Every supported line is the V4 generation,
* so the lists match; the matrix also asserts the union covers the fold's
* whole vocabulary, so a fold case added without a baseline list fails
* loudly instead of going unprobed.
*/
* whole vocabulary, so a fold case added without a baseline list fails loudly instead of going unprobed. */
foldEventTypes: readonly string[]
client: ClientSeam
/**
* The step-boundary identity guard's host seam (src/host/stepIdentity.ts):
/** The step-boundary identity guard's host seam (src/host/stepIdentity.ts):
* the agent-loop file that dispatches the `agent/pre-step` waterfall and
* appends its decision's messages, plus a shipped context plugin proving
* the `prepend` listener option this guard rides on that line.
*/
* the `prepend` listener option this guard rides on that line. */
stepGuard: {
loopFile: string
loopNeedles: readonly string[]
prependProofFile: string
}
/**
* The tag's settings-namespace surface. Every supported line is the V4+
/** The tag's settings-namespace surface. Every supported line is the V4+
* Config-form generation: configuration forms derive from each loader
* entry's own Config schema — the `settings.register` host face is gone
* (probed absent, so the plugin never calls it), and the browser half rides
* the `configForms` service and the Plugins page's keyed
* `plugins.bundle.config` seat.
*/
* the `configForms` service and the Plugins page's keyed `plugins.bundle.config` seat. */
settings: {
/** The settings service source missing the register face. */
serviceFile: string
@@ -179,15 +152,13 @@ export interface Baseline {
transportFile: string
transportPresent: boolean
}
/**
* The balance capsule's host seam (src/host/balance.ts). The facts fold
/** The balance capsule's host seam (src/host/balance.ts). The facts fold
* reads the DeepSeek API-key provider's settings row off the Config-form
* `describe()` projection — the provider row is the entry whose served
* value declares the top-level `apiKeyEnv` credential ref, preferred under
* the settings ids the generations have served it as. The retired
* registered-section `get(ns)` read is probed absent, so a resurrection
* names the seam instead of silently shadowing the projection.
*/
* names the seam instead of silently shadowing the projection. */
balance: {
/** The settings service source the face needles are asserted in. */
settingsFile: string
@@ -201,11 +172,8 @@ export interface Baseline {
}
}
/**
* One support-matrix entry. Every supported line is the V4 generation, so
* the seams are spelled once here; a future line that diverges on a needle
* outgrows the factory (inline its entry instead).
*/
/** One support-matrix entry. Every supported line is the V4 generation, so
* the seams are spelled once here; a future line that diverges on a needle outgrows the factory (inline its entry instead). */
function baseline(id: BaselineId, tag: string, session: string): Baseline {
return {
id,
@@ -317,8 +285,7 @@ export const BASELINES: readonly Baseline[] = [
// baseline pins `0.1.7-rc.2`, the first release of the line with a
// complete npm dependency closure; rc.2 adds `startsSeries` to the first
// `request/header` of a resumed series and the `developer/message`
// tool-registry events. startsSeries is inert to the fold; registry
// messages contribute to injected context.
// tool-registry events. startsSeries is inert to the fold; registry messages contribute to injected context.
// 0.1.7 enforces plugin dsh-peer compatibility at startup and install
// (evaluatePluginCompatibility); this plugin's `>=0.1.7-rc.2` dsh peers
// satisfy every supported line under the gate's includePrerelease check.
@@ -333,7 +300,6 @@ export const BASELINES: readonly Baseline[] = [
// `configForms` transport, the balance settings face, the step-boundary
// guard, and the durable event vocabulary (still the V4 generation — the
// session-format tree carries no v4-to-v5 migration). Cordis stays 4.0.4,
// and the line continues to enforce the plugin dsh-peer gate this
// plugin's `>=0.1.7-rc.2` peers satisfy.
// and the line continues to enforce the plugin dsh-peer gate this plugin's `>=0.1.7-rc.2` peers satisfy.
baseline('v0.2.0-rc.2', 'dsh-v0.2.0-rc.2', '0.2.0-rc.2'),
]
+3 -6
View File
@@ -536,8 +536,7 @@ describe('layoutForest', () => {
assert.equal(linkG.y2, (pointOf.get('g')?.y as number) - AGENT_NODE_R - 10)
assert.equal(linkG.running, false)
assert.equal(layout.links.find(l => l.to === 'b')?.running, true)
// Links carry the family hue of the child's level-1 subtree: g inherits
// a's family (0); b is its own family (1).
// Links carry the family hue of the child's level-1 subtree: g inherits a's family (0); b is its own family (1).
assert.equal(linkG.color, familyHue(0))
assert.equal(layout.links.find(l => l.to === 'b')?.color, familyHue(1))
assert.equal(layout.links.length, 3)
@@ -619,8 +618,7 @@ describe('layoutForest', () => {
test('responsive: child bands interleave with later parent bands (kinship order)', () => {
// Stage 224 → perLevel = 2. Root's children split [p1,p2] / [p3]; p1's
// kids' band lands right after p1's own band — BEFORE p3's band, so a
// family never straddles a stranger's row.
// kids' band lands right after p1's own band — BEFORE p3's band, so a family never straddles a stranger's row.
const layout = layoutForest(forestOf([
['root'],
['p1', 'root'],
@@ -636,8 +634,7 @@ describe('layoutForest', () => {
assert.equal(pointOf.get('p3')?.y, 56 + 3 * 154)
// Kinship interleave: p1's grandchildren sit between p1's band and p3's band.
assert.ok((pointOf.get('k1')?.y as number) < (pointOf.get('p3')?.y as number))
// Links keep their family hue through the interleave: p1's subtree is
// family 0, p3 is family 2.
// Links keep their family hue through the interleave: p1's subtree is family 0, p3 is family 2.
assert.equal(layout.links.find(l => l.to === 'k1')?.color, familyHue(0))
assert.equal(layout.links.find(l => l.to === 'p3')?.color, familyHue(2))
assert.equal(layout.width, 224)
+1 -2
View File
@@ -1,6 +1,5 @@
// Step brief (src/client/brief.ts): node merge/sort, the per-step brief
// (response hit, turn-start detection, opener scan, mid-turn inputs), and
// the tooltip reply previews.
// (response hit, turn-start detection, opener scan, mid-turn inputs), and the tooltip reply previews.
import assert from 'node:assert/strict'
import { describe, test } from 'vitest'
+1 -2
View File
@@ -1,6 +1,5 @@
// Tool-call summaries (src/client/callSummary.ts): argument JSON parsing,
// the description/file_path/path/filePath key priority, and the
// conversation-block scanners.
// the description/file_path/path/filePath key priority, and the conversation-block scanners.
import assert from 'node:assert/strict'
import { describe, test } from 'vitest'
+1 -2
View File
@@ -1,7 +1,6 @@
// Category parts (src/client/categories.ts): the seven-bucket parts builder,
// the official contextBreakdown split (rounding residue on the largest
// category, clamped), the provider-anchored reproportioning, and the Token
// card's billed-usage split.
// category, clamped), the provider-anchored reproportioning, and the Token card's billed-usage split.
import assert from 'node:assert/strict'
import { describe, test } from 'vitest'
+1 -2
View File
@@ -74,8 +74,7 @@ describe('candidates', () => {
const { source } = setup()
const candidates = await source.candidates({ sessionId: 'cmd-empty' }, { query: '', position: 'leading', signal: SIGNAL })
// The localized section replaces the menu's raw source-name title row;
// the localized label and glyph are display-only (older harness builds
// render the bare name).
// the localized label and glyph are display-only (older harness builds render the bare name).
assert.deepEqual(candidates, [{
name: 'context',
label: DICT_EN['cmd.label'],
+3 -6
View File
@@ -33,20 +33,17 @@ function historyEvents(): unknown[] {
]
}
/**
* Arm a test ctx with the harness service faces the way the generation
/** Arm a test ctx with the harness service faces the way the generation
* composes them: the gateway remotes (the traced `remote` facade carrying
* the session page, plus the direct `remote.session` service whose
* undeclared reads throw) and the durable-image loader service. Arm the
* plugin's declared inject (`watchHistoryFaces`) to resolve the page face.
*/
* plugin's declared inject (`watchHistoryFaces`) to resolve the page face. */
export function baselineCtx(): { ctx: TestClientCtx; calls: { page: unknown[]; image: { sessionId: string; attachment: unknown }[] } } {
const ctx = new TestClientCtx()
const calls: { page: unknown[]; image: { sessionId: string; attachment: unknown }[] } = { page: [], image: [] }
const events = historyEvents()
// The remote resolves the ClientResult itself; rows are
// `{type:'event', event}` records.
// The remote resolves the ClientResult itself; rows are `{type:'event', event}` records.
const page = (request: unknown): Promise<unknown> => {
calls.page.push(request)
return Promise.resolve({ ok: true, value: { records: events.map(event => ({ type: 'event', event })) } })
+3 -7
View File
@@ -5,7 +5,6 @@
// face and its response envelope (the remote.session gateway), and the
// MarkdownText chrome prop (required `labels`). These are the seams whose
// drift produced the recurring client-side incidents (issues #8, #12, #26).
//
// The real-code complement — the ACTUAL dsh sources per tag — runs in the
// `compat` vitest project (tests/compat/matrix.spec.ts).
@@ -29,8 +28,7 @@ import { baselineCtx, chatSeat, convNodes } from './baselineFaces'
const kit = makeKit()
// The chrome-prop capture: the plugin must hand EVERY markdown render a
// well-formed `labels` object (the primitives REQUIRE it). Intercept the
// primitive.
// well-formed `labels` object (the primitives REQUIRE it). Intercept the primitive.
const captured = vi.hoisted(() => ({ markdownProps: undefined as Record<string, unknown> | undefined }))
vi.mock('@deepseek-ai/dsh-client-ui-primitives', async () => {
const React = await import('react')
@@ -39,8 +37,7 @@ vi.mock('@deepseek-ai/dsh-client-ui-primitives', async () => {
captured.markdownProps = props
return React.createElement('div', null, String(props.text ?? ''))
},
// The rich-text copy control's glyphs and clipboard writer; the
// Plugin Info card's settings gear.
// The rich-text copy control's glyphs and clipboard writer; the Plugin Info card's settings gear.
IconCopyOutlineRegular: () => React.createElement('span', null),
IconCheckOutlineRegular: () => React.createElement('span', null),
IconSettingsOutlineMedium: () => React.createElement('span', null),
@@ -193,8 +190,7 @@ for (const baseline of BASELINES) {
// Issue #42: a traced proxy can throw on the property READ itself
// ("cannot get property "remote.session" without inject") — arming the
// declared inject with such a hostile facade must leave the slot unset
// (nothing escapes the callback) and the tab renders with its static
// hint instead of crashing.
// (nothing escapes the callback) and the tab renders with its static hint instead of crashing.
const ctx = new TestClientCtx()
ctx.setService('remote', {
get session() { throw new Error('cannot get property "remote.session" without inject') },
+4 -10
View File
@@ -1,8 +1,7 @@
// AgentGraph (src/client/components/agentGraph.tsx) — the Agent network card
// rendered for real against a faithful in-memory sessions face: tree
// rendering with donut/ring geometry, hover inspector, click/keyboard
// navigation, catalog refresh, live snapshot updates, and every degrade arm
// (no service, no anchor, no stats).
// navigation, catalog refresh, live snapshot updates, and every degrade arm (no service, no anchor, no stats).
import { act, createElement as h } from 'react'
import assert from 'node:assert/strict'
@@ -433,10 +432,7 @@ describe('AgentGraph — cold-relative composition fetch', () => {
}
}
/**
* A programmable global fetch recording its reads: POSTs resolve `value`
* (or throw, or hold until released).
*/
/** A programmable global fetch recording its reads: POSTs resolve `value` (or throw, or hold until released). */
function detailFetch(options: { reject?: boolean; head?: unknown; nullValue?: boolean; defer?: boolean } = {}) {
const calls: string[] = []
let release: ((value: unknown) => void) | undefined
@@ -467,14 +463,12 @@ describe('AgentGraph — cold-relative composition fetch', () => {
const { View, face } = makeFetchingView()
const m = await mount(h(View, { sessionId: 'root', self: selfStats() }))
// The cold relative fetched at mount; until the read lands it wears the
// pressure-only fused ring (arc + free outline).
// The cold relative fetched at mount; until the read lands it wears the pressure-only fused ring (arc + free outline).
assert.deepEqual(rpc.calls, ['/api/dsh-context/detail:done'])
assert.equal(query(m.container, 'g[data-agent="done"]').querySelectorAll('circle.lc-agent-seg').length, 2)
// A snapshot tick while the read is in flight re-attaches the SAME
// pending read; its duplicate landing settles on the identity bail-out
// instead of re-rendering.
// pending read; its duplicate landing settles on the identity bail-out instead of re-rendering.
await act(async () => {
face.setState(family())
})
@@ -4,8 +4,7 @@
// currency shows with the account's first currency as fallback, and the
// non-zero parts of the breakdown (topped-up / granted; the pill itself
// carries the total) ride the page's hover tip via the pill's data
// attributes. The route read is stubbed per test; memory and storage are
// reset between tests.
// attributes. The route read is stubbed per test; memory and storage are reset between tests.
import { createElement as h } from 'react'
import assert from 'node:assert/strict'
@@ -85,8 +84,7 @@ describe('BalanceCapsule', () => {
assert.equal(query(m.container, '.lc-ov-balance-value')?.textContent, '$12.50')
// The breakdown lives in the pill's data attributes for the page's hover
// tip (no native `title`): the pill already carries the total, so only
// the non-zero parts ride the bubble — the topped-up line leads the
// granted one, and the tip drops below the pill.
// the non-zero parts ride the bubble — the topped-up line leads the granted one, and the tip drops below the pill.
assert.equal(pill?.getAttribute('title'), null)
assert.equal(pill?.getAttribute('data-lc-tip'), 'Topped-up balance: $10.00\nGranted balance: $2.50')
assert.equal(pill?.getAttribute('data-lc-tip-side'), 'bottom')
@@ -119,8 +117,7 @@ describe('BalanceCapsule', () => {
test('the zh locale shows CNY with localized breakdown labels', async () => {
stubRoute({ ok: true, value: WIRE_BALANCE })
// The kit's `t` is the harness's locale-bound translate (zh), while the
// ctx's locale service drives the currency pick.
// The kit's `t` is the harness's locale-bound translate (zh), while the ctx's locale service drives the currency pick.
const Capsule = makeBalanceCapsule(asClientCtx(new TestClientCtx({ locale: 'zh' })), makeKit('zh'))
const m = await mount(h(Capsule, {}))
await flush()
+4 -8
View File
@@ -46,8 +46,7 @@ afterEach(() => {
vi.restoreAllMocks()
vi.unstubAllGlobals()
// The cold-start share (timelineSource.ts) is page-lifetime per session:
// drop it so one test's cold read never leaks its state into a later test
// that reuses the same session id.
// drop it so one test's cold read never leaks its state into a later test that reuses the same session id.
resetTimelineDetailStores()
})
@@ -302,8 +301,7 @@ describe('ContextModal', () => {
useContextModal: boundModalHook(sid),
useProjection: () => undefined,
}))
// Relocate the mount container under the frame before opening, so the
// measure walks the real ancestor chain.
// Relocate the mount container under the frame before opening, so the measure walks the real ancestor chain.
seat.appendChild(m.container)
await act(async () => {
modalStoreOf(sid).set(true)
@@ -312,8 +310,7 @@ describe('ContextModal', () => {
assert.equal(backdrop.style.left, '280px')
assert.equal(backdrop.style.right, '360px')
// A sidebar rewrite while open (the right panel closed here) is followed
// through the observer on both edges.
// A sidebar rewrite while open (the right panel closed here) is followed through the observer on both edges.
await act(async () => {
frame.style.gridTemplateColumns = '56px minmax(0px, 1fr) minmax(0px, 0px)'
})
@@ -506,8 +503,7 @@ describe('ContextModal — the split generation', () => {
// The face lands mid-open (cordis replays the pending inject): the modal
// re-renders, the fetcher builds, and the open epoch fetches by itself.
// The face rides INSIDE the `remote` facade; the `remote.session` service
// key stays hostile, as on the real host.
// The face rides INSIDE the `remote` facade; the `remote.session` service key stays hostile, as on the real host.
await act(async () => {
ctx.setService('remote.session', { get page() { throw new Error('cannot get property "remote.session" without inject') } })
ctx.setService('remote', {
+7 -14
View File
@@ -22,8 +22,7 @@ import { DICT_EN } from '../../../src/client/i18n'
import { TestClientCtx, TestLocale, asClientCtx } from '../helpers/harness'
import { click, flush, hover, makeKit, mount, query, queryAll, silenceWindowErrors, text, unhover, until } from '../helpers/kit'
// pluginInfo's npm-registry probe stays inert (and '0.0.0-dev' short-circuits
// it anyway).
// pluginInfo's npm-registry probe stays inert (and '0.0.0-dev' short-circuits it anyway).
vi.stubGlobal('fetch', () => Promise.resolve({ ok: false } as Response))
const kit = makeKit()
@@ -32,8 +31,7 @@ afterEach(() => {
vi.restoreAllMocks()
vi.unstubAllGlobals()
// The cold-start share (timelineSource.ts) is page-lifetime per session:
// drop it so one test's cold read never leaks its failed/ready state into
// a later test that reuses the same session id.
// drop it so one test's cold read never leaks its failed/ready state into a later test that reuses the same session id.
resetTimelineDetailStores()
})
@@ -195,8 +193,7 @@ describe('ContextView — projection guards', () => {
assert.ok(text(m.container).includes(DICT_EN['overview.used']))
// The Token card's center is the chat stats line's whole-session billed
// total off the official tokenUsage projection (100 + 200 + 0 + 50), and
// its corner shows the line's own cache-hit rate two decimals deep
// (200 / 300, truncated).
// its corner shows the line's own cache-hit rate two decimals deep (200 / 300, truncated).
const tokensCard = queryAll(m.container, '.lc-head > .lc-col-donut')[0]
assert.equal(query(tokensCard, '.lc-donut-center b').textContent, '350')
assert.equal(query(tokensCard, '.lc-card-rate').textContent, 'Cache Hit 66.66%')
@@ -871,8 +868,7 @@ describe('ContextView — file activity card', () => {
const card = queryAll(m.container, '.lc-card').find(c => text(c).includes(DICT_EN['files.title']))
assert.ok(card !== undefined)
const name = query(query(card, '.lc-fa-row'), '.lc-fa-file')
// The preview affordance leads where the column exists; the system open
// stays unreached.
// The preview affordance leads where the column exists; the system open stays unreached.
assert.equal(name.getAttribute('title'), DICT_EN['files.preview'])
await click(name)
assert.deepEqual(previewed, ['dsh-resource://file/session/sv-preview/src/a.ts'])
@@ -1014,8 +1010,7 @@ describe('ContextView — targeted content fetch and image loading', () => {
},
},
})
// The direct `remote.session` service is hostile, as on the real host:
// the face resolves only through the declared inject.
// The direct `remote.session` service is hostile, as on the real host: the face resolves only through the declared inject.
ctx.setService('remote.session', { get page() { throw new Error('cannot get property "remote.session" without inject') } })
watchHistoryFaces(asClientCtx(ctx))
const View = makeView(ctx)
@@ -1487,8 +1482,7 @@ describe('ContextView — the split generation (slim head + detail channel)', ()
const ctx = slimCtx(async () => ({ ok: true, value: slimDetail() }))
const View = makeView(ctx)
// The jump is armed BEFORE the view mounts (the chat action relay) — the
// detail read is still pending, so the pin must wait for it (not consume
// and clamp against an empty record list).
// detail read is still pending, so the pin must wait for it (not consume and clamp against an empty record list).
requestContextFocus('sv-slim-jump', 4)
const m = await mount(h(View, { sessionId: 'sv-slim-jump', useProjection: projectionsFor(slimHead()) }))
assert.equal(queryAll(m.container, '.lc-bar-selected').length, 0, 'nothing pins before the detail')
@@ -1515,8 +1509,7 @@ describe('ContextView — the op-log generation (fileOps on the detail payload)'
nodes: [],
droppedNodes: 0,
archive: [],
// The op log covers the full session — the conversation window
// join plays no role in this card on this generation.
// The op log covers the full session — the conversation window join plays no role in this card on this generation.
fileOps: [
{ seq: 1, path: '/ws/README.md', kind: 'read', tool: 'read', err: false, added: 0, removed: 0, read: { start: 1, count: 12 } },
{ seq: 2, path: '/ws/src/a.ts', kind: 'write', tool: 'edit', err: false, added: 3, removed: 1 },
+3 -6
View File
@@ -1,7 +1,6 @@
// The Insights page's date-range picker (src/client/components/dateRange.tsx)
// — the two-click range in either order, the highlighted band and edges, the
// portaled panel's own seat and every way out of it, and the month nav plus
// the grid's keyboard cursor.
// portaled panel's own seat and every way out of it, and the month nav plus the grid's keyboard cursor.
import { act, createElement as h } from 'react'
import assert from 'node:assert/strict'
@@ -322,8 +321,7 @@ describe('the keyboard cursor', () => {
describe('the panel seat', () => {
test('sits under its trigger, inside the viewport, and flips when it would fall off', async () => {
// jsdom measures every element as 0; drive the panel's own box, then put
// the prototype's own descriptors back (deleting them would leave every
// later measurement NaN).
// the prototype's own descriptors back (deleting them would leave every later measurement NaN).
const size = { w: 240, h: 300 }
const own = (name: string): PropertyDescriptor | undefined => Object.getOwnPropertyDescriptor(HTMLElement.prototype, name)
const width = own('offsetWidth')
@@ -336,8 +334,7 @@ describe('the panel seat', () => {
Object.defineProperty(HTMLElement.prototype, 'offsetHeight', { configurable: true, get: () => size.h })
try {
const { m } = await open()
// The panel measures its anchor wrapper (the trigger's own box): jsdom's
// rects are all zeros, so drive the one it reads.
// The panel measures its anchor wrapper (the trigger's own box): jsdom's rects are all zeros, so drive the one it reads.
const rect = { left: 800, right: 900, top: 680, bottom: 700 }
query(m.container, '.lc-dp').getBoundingClientRect = () =>
({ ...rect, x: rect.left, y: rect.top, width: 100, height: 20, toJSON: () => '' }) as DOMRect
+1 -2
View File
@@ -1,6 +1,5 @@
// Donut (src/client/components/donut.tsx) rendered with real React:
// proportional SVG segments around the center label, with the empty ring
// fallback and hostile-value skips.
// proportional SVG segments around the center label, with the empty ring fallback and hostile-value skips.
import { createElement as h, useState } from 'react'
import assert from 'node:assert/strict'
@@ -1,8 +1,7 @@
// ErrorBoundary (src/client/components/errorBoundary.tsx): real subtree
// errors degrade to the styled error card; Retry resets the boundary.
// React logs caught errors to console and replays the failed render through a
// fake DOM event — silenced with a window-error preventer and a spy, never
// mocked.
// fake DOM event — silenced with a window-error preventer and a spy, never mocked.
import { createElement as h } from 'react'
import assert from 'node:assert/strict'
+3 -6
View File
@@ -96,8 +96,7 @@ describe('EventList', () => {
assert.ok(text(rows[0]).includes('Context Injection'))
assert.equal(queryAll(rows[0], '.lc-event-tokens').length, 0)
// Foreign kind: EVENT_ICONS miss → '•' fallback, and the kind label
// resolves through the locale chain to the key itself.
// Foreign kind: EVENT_ICONS miss → '•' fallback, and the kind label resolves through the locale chain to the key itself.
assert.equal(query(rows[1], '.lc-event-icon').textContent, '•')
assert.equal(query(rows[1], '.lc-kind').textContent, 'kind.mystery')
@@ -133,10 +132,8 @@ describe('EventList', () => {
assert.equal(labels[0].getAttribute('title'), '')
assert.equal(labels[1].getAttribute('title'), '')
// Newest first: labels[0] is the prune, labels[1] the inject.
// Force an empty-text overflow on the prune label (the
// `el.textContent || ''` fallback arm) and a real overflow on the
// inject label, then resize.
// Newest first: labels[0] is the prune, labels[1] the inject. Force an empty-text overflow on the prune label (the
// `el.textContent || ''` fallback arm) and a real overflow on the inject label, then resize.
Object.defineProperty(labels[0], 'scrollWidth', { value: 100, configurable: true })
Object.defineProperty(labels[1], 'scrollWidth', { value: 100, configurable: true })
labels[0].textContent = ''
+2 -4
View File
@@ -532,15 +532,13 @@ describe('FileCard — workspace display and system open', () => {
onPreview: e => { previewed.push(e); return true },
onOpen: p => { opened.push(p) },
}))
// The preview affordance leads: the title says so, and the click never
// reaches the system opener.
// The preview affordance leads: the title says so, and the click never reaches the system opener.
const a = baseOf(rowOf(m.container, '/repo/src/a.ts'))
assert.equal(a.getAttribute('title'), 'Preview in the sidebar')
await click(a)
assert.deepEqual(previewed.map(e => e.path), ['/repo/src/a.ts'])
assert.equal(opened.length, 0)
// A refusal (no preview type claims it, no surface mounted) still opens on
// the system rather than becoming an inert click.
// A refusal (no preview type claims it, no surface mounted) still opens on the system rather than becoming an inert click.
await m.unmount()
const m2 = await mount(h(FileCard, {
activity: workspaceActivity(),
+3 -6
View File
@@ -1,6 +1,5 @@
// The activity heatmap (src/client/components/heatmap.tsx): grid math off
// the injected today key, depth levels, day pinning, future cells, and the
// empty/degraded paths — rendered with real React.
// the injected today key, depth levels, day pinning, future cells, and the empty/degraded paths — rendered with real React.
import { createElement as h } from 'react'
import assert from 'node:assert/strict'
@@ -82,8 +81,7 @@ describe('Heatmap', () => {
const spans = queryAll(m.container, 'span.lc-heat-cell')
assert.ok(spans.length > 0)
assert.ok(spans.some(s => s.className.includes('lc-heat-future')), 'future placeholders present')
// Weekday labels sit on the rows they name (Sunday-first: row 1 = Mon):
// Mon/Wed/Fri carry their names, the rest blank.
// Weekday labels sit on the rows they name (Sunday-first: row 1 = Mon): Mon/Wed/Fri carry their names, the rest blank.
const wds = queryAll(m.container, '.lc-heat-wd')
assert.deepEqual(wds.map(w => w.textContent), ['', 'Mon', '', 'Wed', '', 'Fri', ''])
await m.unmount()
@@ -92,8 +90,7 @@ describe('Heatmap', () => {
test('month labels mark the columns where a month begins', async () => {
const days = { '2026-09-16': { tokens: 10, requests: 1, sessions: 1, cost: null } }
// The default 8-week window runs Sunday 2026-07-26 → 2026-09-13: July
// began outside it, August begins in the Jul 26 column (Sat Aug 1) and
// September in the Aug 30 column (Tue Sep 1).
// began outside it, August begins in the Jul 26 column (Sat Aug 1) and September in the Aug 30 column (Tue Sep 1).
const m = await mount(h(Heatmap, { days, today: TODAY }))
assert.deepEqual(queryAll(m.container, '.lc-heat-mon').map(s => s.textContent), ['Aug', 'Sep'])
await m.unmount()
+1 -2
View File
@@ -1,7 +1,6 @@
// images.tsx — imageRefOf narrowing (pure) plus ImageCard and its preview
// wiring, rendered with real React. The preview itself is the platform
// ImageLightbox; its dialog behavior is covered upstream, so only the
// plugin-owned open/close wiring is pinned here.
// ImageLightbox; its dialog behavior is covered upstream, so only the plugin-owned open/close wiring is pinned here.
import { createElement as h } from 'react'
import assert from 'node:assert/strict'
@@ -99,8 +99,7 @@ async function openPanel(
...props,
}
const m = await mount(h(Panel, allProps))
// The price book's first fetch resolves a microtask or two behind the
// mount; a second act window keeps its notify inside act.
// The price book's first fetch resolves a microtask or two behind the mount; a second act window keeps its notify inside act.
await flush()
return { m, Panel }
}
@@ -144,8 +143,7 @@ describe('OverviewPanel', () => {
assert.ok(text(m.container).includes('Context Insights'))
// The page chrome: the section's one scroll region carries the centered
// content column — the page heading, then the 1:1 first row (the KPI
// band beside the last-7-days usage chart), the aggregate stats pair,
// and the insight/session body.
// band beside the last-7-days usage chart), the aggregate stats pair, and the insight/session body.
const page = query(m.container, 'section.lc-ov-page')
assert.equal(page.getAttribute('aria-label'), 'Context Insights')
const scrollRows = [...query(m.container, '.lc-ov-pagecontent').children].map(el => el.className.split(' ')[0])
@@ -454,8 +452,7 @@ describe('OverviewPanel', () => {
test('the settings chip beside the title runs the preferences jump', async () => {
// The harness chrome the jump drives (settingsJump.ts): the Plugins panel
// entry first, then the bundle card's open control (the one non-switch
// button inside the card), both click-tracked.
// entry first, then the bundle card's open control (the one non-switch button inside the card), both click-tracked.
const clicks: string[] = []
const chrome = document.createElement('div')
chrome.innerHTML = `
@@ -472,8 +469,7 @@ describe('OverviewPanel', () => {
const ctx = makeCtx()
const { m } = await openPanel(ctx)
const chip = query<HTMLButtonElement>(m.container, '.lc-ov-settings')
// The chip rides the heading, directly after the title (the long hint
// moved onto the hover title).
// The chip rides the heading, directly after the title (the long hint moved onto the hover title).
assert.equal(chip.textContent, 'Plugin Settings')
assert.equal(chip.title, 'Open plugin settings')
const head = query(m.container, '.lc-ov-pagehead')
@@ -443,8 +443,7 @@ describe('OverviewSkills', () => {
const detail = query(m.container, '.lc-ov-skill-detail')
assert.equal(detail.closest('.lc-ov-skill-unit'), null, 'no inline seat without the pinned row')
assert.equal(queryAll(detail, '.lc-skilld-head').length, 1, 'the detached seat carries the head')
// All twenty-five generated names match 'skill' — twenty rows plus the
// overflow line counting the filtered set.
// All twenty-five generated names match 'skill' — twenty rows plus the overflow line counting the filtered set.
await actType(input, 'skill')
assert.equal(queryAll(m.container, 'button.lc-ov-skill').length, 20)
assert.ok(text(m.container).includes('5 more skills'), 'the overflow counts the filtered set')
@@ -1,8 +1,7 @@
// The Context Insights panel's aggregate Token Stats card
// (src/client/components/overviewTokens.tsx) rendered with real React: the
// donut centered on the range's merged billed total and split by the SAME
// composition categories as the Context tab's Token card — the per-session
// `billedParts` estimates folded by tokenPartsOf.
// composition categories as the Context tab's Token card — the per-session `billedParts` estimates folded by tokenPartsOf.
import { createElement as h } from 'react'
import assert from 'node:assert/strict'
@@ -1,8 +1,7 @@
// The first row's usage chart (src/client/components/overviewUsage.tsx):
// the fixed last-7-days window off the injected today key, the two series'
// independent maxima, bar heights, the tick rails and gridlines framed off
// the same maxima, unpriced/zero days, and the empty and degraded paths —
// rendered with real React.
// the same maxima, unpriced/zero days, and the empty and degraded paths — rendered with real React.
import { createElement as h } from 'react'
import assert from 'node:assert/strict'
+1 -2
View File
@@ -1,7 +1,6 @@
// PluginInfo (src/client/components/pluginInfo.tsx): static metadata rows
// plus the live npm latest-version check. fetch is stubbed per test and the
// modules are re-imported fresh (vi.resetModules) so the baked-in version
// define and the fetch TTL cache both start clean.
// modules are re-imported fresh (vi.resetModules) so the baked-in version define and the fetch TTL cache both start clean.
import { createElement as h } from 'react'
import assert from 'node:assert/strict'
@@ -368,8 +368,7 @@ describe('RequestDetail brief section', () => {
44: { kind: 'assistant', seq: 44, blocks: [{ kind: 'tool-call', name: 'write', argsRaw: '{"file_path":"a.ts"}' }] },
}
// One row per input would need 11 rows; instead render them as one inputs
// list — chips are the unit under test (3 shown + overflow), so render
// batches through separate mounts.
// list — chips are the unit under test (3 shown + overflow), so render batches through separate mounts.
const render = async (list: SurfaceNode[]) => {
const m = await mount(h(RequestDetail, {
request: req({}),
@@ -412,8 +411,7 @@ describe('RequestDetail brief section', () => {
assert.ok(chipText(m6, 0).includes('Skill · ponytail') && chipText(m6, 0).includes('notes inside'))
await m6.unmount()
// A category outside the fold's vocabulary (host drift) degrades into the
// user tail — a plain text row, never a throw.
// A category outside the fold's vocabulary (host drift) degrades into the user tail — a plain text row, never a throw.
const m7 = await render([surfaceNode({ seq: 43, cat: 'mystery' as never, text: 'survivor' })])
assert.equal(chipText(m7, 0), 'survivor', 'hostile cat renders its text plainly')
await m7.unmount()
@@ -425,8 +423,7 @@ describe('RequestDetail brief section', () => {
const m11 = await render([surfaceNode({ seq: 47, cat: 'skill', skill: 'grilling', tool: 'skill' })])
assert.equal(chipText(m11, 0), 'Skill · grilling')
await m11.unmount()
// The catalog digest tags its form; a nameless, formless skill row
// degrades to the context label.
// The catalog digest tags its form; a nameless, formless skill row degrades to the context label.
const m11b = await render([
surfaceNode({ seq: 471, cat: 'skill', form: 'catalog' }),
surfaceNode({ seq: 472, cat: 'skill' }),
+2 -4
View File
@@ -1,7 +1,6 @@
// The preference card (src/client/components/settingsCard.tsx): the
// Plugins-page seat rendered with real React against the real DICT_EN
// strings; the select rows open the REAL Menu primitive (portaled into
// document.body) and pick through it.
// strings; the select rows open the REAL Menu primitive (portaled into document.body) and pick through it.
import { createElement as h } from 'react'
import assert from 'node:assert/strict'
@@ -75,8 +74,7 @@ describe('PluginConfigCard (the Plugins-page seat)', () => {
assert.equal(selects[0].getAttribute('aria-expanded'), 'false')
assert.equal(document.body.querySelector('[role="menu"]'), null)
// The insights-entry row follows the placement one; its pick writes the
// visibility field.
// The insights-entry row follows the placement one; its pick writes the visibility field.
assert.ok(text(m.container).includes(DICT_EN['settings.insightsEntry']))
assert.ok(text(selects[1]).includes(DICT_EN['insightsEntry.show']))
await click(selects[1])
+1 -2
View File
@@ -1,6 +1,5 @@
// SliceList (src/client/components/sliceList.tsx) rendered with real React:
// the donut legend rows — the primary line (color dot, label, bold share)
// over the secondary quantity line.
// the donut legend rows — the primary line (color dot, label, bold share) over the secondary quantity line.
import { createElement as h, useState } from 'react'
import assert from 'node:assert/strict'
+1 -2
View File
@@ -1,6 +1,5 @@
// StackedBar + Legend (src/client/components/stackedBar.tsx) rendered with
// real React through a state-holding hover harness, plus direct mounts for
// the scale/free/reserve/tooltip branch matrix.
// real React through a state-holding hover harness, plus direct mounts for the scale/free/reserve/tooltip branch matrix.
import { createElement as h, useState } from 'react'
import assert from 'node:assert/strict'
+2 -4
View File
@@ -10,8 +10,7 @@
// book (the store never reaches the network). The connector geometry is
// pinned through measureFlow's unit tests (jsdom has no layout); the event
// rows themselves live on the events card (contextView.spec.ts).
// `countsOfRecords` and `toolTallyOf` derive the tallies the split
// generation's wire head carries, pinned here.
// `countsOfRecords` and `toolTallyOf` derive the tallies the split generation's wire head carries, pinned here.
import { act, createElement as h } from 'react'
import assert from 'node:assert/strict'
@@ -100,8 +99,7 @@ describe('countsOfRecords (the inline generation derivation)', () => {
[
ev('inject'), ev('inject'), ev('inject'), ev('compaction'), ev('compaction'), ev('prune'), ev('model'), ev('mode'),
// Skill loads and a `/name` invocation count by DISTINCT name; the
// available-skills catalog digest rides an untagged inject event and
// never lands in the tally.
// available-skills catalog digest rides an untagged inject event and never lands in the tally.
skillEv('grilling'), skillEv('grilling'), skillEv('ponytail'),
{ ...ev('inject'), name: 'available-skills' },
// Hostile shapes skip: a nameless tag and a non-string name.
+1 -2
View File
@@ -2,8 +2,7 @@
// React: the active-time donut (TTFT + generation vs tools vs overhead) and
// the pct-led slice rows with call counts and true durations — plus the
// bottom session-time strip (the fold's per-step spans packed by their share
// of the active time in occurrence order, joined into the hover link by
// KIND), and the empty and hostile-timing degrades.
// of the active time in occurrence order, joined into the hover link by KIND), and the empty and hostile-timing degrades.
import { createElement as h } from 'react'
import assert from 'node:assert/strict'
+1 -2
View File
@@ -1,7 +1,6 @@
// StatsTokens (src/client/components/statsTokens.tsx) rendered with real
// React: the donut centered on the chat stats line's whole-session billed
// total, sliced by composition category — the prompt side shared by the
// estimated ratios, output exact.
// total, sliced by composition category — the prompt side shared by the estimated ratios, output exact.
import { createElement as h } from 'react'
import assert from 'node:assert/strict'
+5 -10
View File
@@ -4,8 +4,7 @@
// is its share of the active time; idle time takes no track), the fixed
// zero/quartile/full ticks, the kind-keyed hover link (every span of a kind
// lights together while the tip answers only the strip's OWN pointer), and
// the hostile-span sanitize — an unknown kind or a NaN/inverted instant drops
// out whole instead of poisoning the axis.
// the hostile-span sanitize — an unknown kind or a NaN/inverted instant drops out whole instead of poisoning the axis.
import { createElement as h } from 'react'
import assert from 'node:assert/strict'
@@ -43,8 +42,7 @@ describe('TimingStrip', () => {
const segs = segsOf(m.container)
assert.equal(segs.length, 5)
// Width reads HOW LONG (5/10/5/15/5s of the 40s active total), position
// reads the running share — the idle gaps (21s→26s, 41s→46s) take no
// track: tools sits at 50%, not at the wall-clock 62.5%.
// reads the running share — the idle gaps (21s→26s, 41s→46s) take no track: tools sits at 50%, not at the wall-clock 62.5%.
assert.deepEqual(leftsOf(m.container), [0, 12.5, 37.5, 50, 87.5])
assert.deepEqual(widthsOf(m.container), [12.5, 25, 12.5, 37.5, 12.5])
assert.deepEqual(segs.map(s => s.style.background), [
@@ -54,8 +52,7 @@ describe('TimingStrip', () => {
'var(--color-teal-500)',
'var(--color-slate-400)',
])
// The axis: zero, the three quartiles, and the full span of the 40s
// ACTIVE total (not the 50s wall window).
// The axis: zero, the three quartiles, and the full span of the 40s ACTIVE total (not the 50s wall window).
const ticks = queryAll(m.container, '.lc-tstrip-tick')
assert.deepEqual(ticks.map(t => t.textContent), ['0', '10.0s', '20.0s', '30.0s', '40.0s'])
assert.deepEqual(ticks.map(t => (t as HTMLElement).style.left), ['0%', '25%', '50%', '75%', '100%'])
@@ -77,8 +74,7 @@ describe('TimingStrip', () => {
assert.equal(wrapper.length, 1)
assert.ok(wrapper[0].className.includes('animate-lc-stacked-in'))
assert.ok(wrapper[0].className.includes('motion-reduce:animate-none'))
// Every band sits INSIDE the wrapper (their percent offsets keep the bar's own box) and carries no
// animation of its own.
// Every band sits INSIDE the wrapper (their percent offsets keep the bar's own box) and carries no animation of its own.
assert.equal(wrapper[0].querySelectorAll('.lc-tstrip-seg').length, 300)
assert.deepEqual(queryAll(m.container, '.lc-tstrip-seg').filter(s => /animate-/.test(s.className)), [])
await m.unmount()
@@ -114,8 +110,7 @@ describe('TimingStrip', () => {
})
test('every span of the hovered KIND lights together and dims the rest, WITHOUT floating the tip (the mirror rule)', async () => {
// A second ttft span (the next step's wait, sitting inside the wall-clock
// idle window — it packs all the same).
// A second ttft span (the next step's wait, sitting inside the wall-clock idle window — it packs all the same).
const spans: TimingSpan[] = [...SPANS, { kind: 'ttft', start: 21_000, end: 26_000 }]
const m = await mount(h(Strip, { spans, hoverKey: 'ttft' }))
const on = queryAll(m.container, '.lc-tstrip-seg-on')
+1 -2
View File
@@ -1,7 +1,6 @@
// UpgradeGate (src/client/components/upgradeGate.tsx) — the baseline-gate
// modal rendered for real in jsdom: version rows, backdrop/card/Escape/OK/×
// dismissal, and the per-session dismissal ledger across remounts and
// in-place session switches.
// dismissal, and the per-session dismissal ledger across remounts and in-place session switches.
import { createElement as h } from 'react'
import assert from 'node:assert/strict'
+2 -4
View File
@@ -2,8 +2,7 @@
// (exact, case-insensitive, and the model-side resolution tiers behind the
// cross-provider fallback), the USD→CNY conversion at the fixed 1 CNY = 0.15
// USD, the null degradations, the numOf coercion of garbage bucket fields,
// the money/rate formatting, and the deep merge behind the stats board's
// family-scope cost cells.
// the money/rate formatting, and the deep merge behind the stats board's family-scope cost cells.
import assert from 'node:assert/strict'
import { describe, test } from 'vitest'
@@ -148,8 +147,7 @@ describe('priceIndexOf / the model-side resolution tiers', () => {
// The live-registry shape: azure re-lists `deepseek-v4-pro` flat (no
// cache_read published) and its `@ai-sdk/azure` package is the only
// self-named one — DeepSeek rides `@ai-sdk/openai-compatible`, so the
// model-side tiers would hand the id to azure. The `deepseek-` spelling
// prices from DeepSeek's own listing instead.
// model-side tiers would hand the id to azure. The `deepseek-` spelling prices from DeepSeek's own listing instead.
const AZURE_FLAT = { hit: 1.74, miss: 1.74, write: 1.74, out: 3.48 }
const hostBook = bookOf({
deepseek: { 'deepseek-v4-pro': PRO },
+1 -2
View File
@@ -1,7 +1,6 @@
// dnaOf (src/client/dna.ts): the DNA strip's per-item decomposition — the
// system prompt and the header epoch's tool schemas lead, the message flow
// follows in seq order: the order the model reads the context, which is also
// the order items joined it.
// follows in seq order: the order the model reads the context, which is also the order items joined it.
import assert from 'node:assert/strict'
import { describe, test } from 'vitest'
+3 -6
View File
@@ -415,8 +415,7 @@ describe('activityOf — nested Code-Mode (PTC) calls', () => {
})])
assert.deepEqual(a.entries.map(e => e.path), ['/b.ts', '/a.ts'])
assert.equal(a.entries[1].ops[0].err, true)
// The parent's stamp locates the op; a failed program does not fail its
// earlier successful reads.
// The parent's stamp locates the op; a failed program does not fail its earlier successful reads.
const ok = a.entries[0].ops[0]
assert.equal(ok.err, false)
assert.equal(ok.gone, 40)
@@ -507,8 +506,7 @@ describe('activityOf — search meta attribution', () => {
meta: { shape: 'paths', truncated: false, total: 1, paths: ['/old.ts'] },
}),
])
// The searched target rows too: '/src' (the narrowed path) and the two
// pathless patterns ride ahead of their hit files.
// The searched target rows too: '/src' (the narrowed path) and the two pathless patterns ride ahead of their hit files.
assert.deepEqual(a.entries.map(e => e.path), ['old', '/old.ts', 'broad', '/src', '/src/a.ts', '/src/b.ts'])
const byPath = new Map(a.entries.map(e => [e.path, e]))
const srcA = byPath.get('/src/a.ts')!
@@ -681,8 +679,7 @@ describe('glyphOf', () => {
assert.ok(g.text !== undefined, `badge text color missing for ${base}`)
}
// The text color contrasts with the fill: near-black on light shades (the
// JS yellow, shell green, rust peach), white on dark ones (including the
// kotlin lilac, just under the threshold).
// JS yellow, shell green, rust peach), white on dark ones (including the kotlin lilac, just under the threshold).
assert.equal(glyphOf('/x/index.js', 'text').text, '#1f2328')
assert.equal(glyphOf('/x/deploy.sh', 'text').text, '#1f2328')
assert.equal(glyphOf('/x/lib.rs', 'text').text, '#1f2328')
+1 -2
View File
@@ -1,6 +1,5 @@
// Display formatting (src/client/format.ts): the k/M/B suffix style, byte
// sizes, the cache-hit truncation, locale-safe time, locale-unit durations,
// and leading slice shares.
// sizes, the cache-hit truncation, locale-safe time, locale-unit durations, and leading slice shares.
import assert from 'node:assert/strict'
import { describe, test } from 'vitest'
+5 -10
View File
@@ -2,8 +2,7 @@
// plugin's `apply` consumes. Every member implements the DOCUMENTED contract
// (cordis effect/inject semantics, locale fallback chain, slot registry,
// sessions scope) rather than a test-specific stub — registrations
// and disposals behave the way the real harness's do, so specs assert through
// the same seams the production runtime uses.
// and disposals behave the way the real harness's do, so specs assert through the same seams the production runtime uses.
import type { ClientCtx, LocaleService, SlotRegistration, SlotsService } from '../../../src/client/services'
import { makeTranslate } from './kit'
@@ -63,12 +62,10 @@ export interface TestClientCtxOptions {
services?: Record<string, unknown>
}
/**
* A client ctx with cordis semantics: `inject` runs its callback once every
/** A client ctx with cordis semantics: `inject` runs its callback once every
* requested service exists (services are armed via options.services or
* setService — arming later replays pending callbacks, like cordis), and
* `effect` collects disposers that `dispose()` runs LIFO.
*/
* `effect` collects disposers that `dispose()` runs LIFO. */
export class TestClientCtx {
readonly slots = new TestSlots()
readonly locale: TestLocale
@@ -101,12 +98,10 @@ export class TestClientCtx {
}
}
/**
* Cordis semantics: an inject gets its fiber handle as soon as it is
/** Cordis semantics: an inject gets its fiber handle as soon as it is
* declared (PENDING while a service is still missing) — disposing it either
* cancels the pending wait or unwinds the loaded callback, and the loaded
* disposer also belongs to the context's own dispose (LIFO). Either path
* retires it, so the two lifetimes never run it twice.
* disposer also belongs to the context's own dispose (LIFO). Either path retires it, so the two lifetimes never run it twice.
*/
inject(deps: string[] | Record<string, unknown>, cb: (ctx: TestClientCtx) => (() => void) | void): { dispose: () => Promise<void> } {
const list = Array.isArray(deps) ? deps : Object.keys(deps)
+9 -20
View File
@@ -1,7 +1,6 @@
// Client test kit: real dictionaries, real React rendering, and a faithful
// harness-context implementation (the documented cordis/locale/slots
// contracts, not mocks of plugin code). Shared by every client spec so each
// test file stays small and stateless.
// contracts, not mocks of plugin code). Shared by every client spec so each test file stays small and stateless.
import { act, type ReactElement, type ReactNode } from 'react'
import { createRoot, type Root } from 'react-dom/client'
@@ -20,11 +19,9 @@ export function surfaceNodeAt(seq: number, over: Partial<SurfaceNode> = {}): Sur
return { seq, cat: 'user', tokens: 1, ...over }
}
/**
* The harness locale chain: active-locale dictionary → en → the key itself,
/** The harness locale chain: active-locale dictionary → en → the key itself,
* with `{name}` interpolation. Built over the plugin's REAL dictionaries, so
* assertions exercise the shipped strings rather than a test copy.
*/
* assertions exercise the shipped strings rather than a test copy. */
export function makeTranslate(active: 'en' | 'zh' = 'en', dicts?: Record<string, Record<string, string>>): Translate {
const table = dicts ?? { zh: DICT_ZH, en: DICT_EN }
return (key, params) => {
@@ -76,13 +73,11 @@ export async function flush(): Promise<void> {
await act(async () => {})
}
/**
* Poll until the predicate holds, one short act() scope per tick: timers and
/** Poll until the predicate holds, one short act() scope per tick: timers and
* promise chains only get to run while a scope's sleep is awaiting, so the
* state updates they land are act-attributed (no React act() warnings) and
* committed when the scope exits — the check between ticks sees them. Real
* timers ride the store's own debounce; `message` labels the give-up failure.
*/
* timers ride the store's own debounce; `message` labels the give-up failure. */
export async function until(fn: () => boolean, message: string): Promise<void> {
for (let i = 0; i < 400; i++) {
await act(async () => {
@@ -94,13 +89,10 @@ export async function until(fn: () => boolean, message: string): Promise<void> {
throw new Error(message)
}
/**
* Silence a deliberately-thrown render error: React 18 dev replays a failed
/** Silence a deliberately-thrown render error: React 18 dev replays a failed
* render through a fake DOM event, which jsdom reports as an uncaught window
* error and vitest forwards to an uncaughtException — a user error listener
* that prevents the default keeps the throw inside the test. Returns the
* cleanup that restores normal error reporting.
*/
* that prevents the default keeps the throw inside the test. Returns the cleanup that restores normal error reporting. */
export function silenceWindowErrors(): () => void {
const onError = (e: Event) => { e.preventDefault() }
window.addEventListener('error', onError)
@@ -128,11 +120,8 @@ export async function click(el: HTMLElement): Promise<void> {
})
}
/**
* Pointer hover. React 18 synthesizes onMouseEnter/onMouseLeave from the
* BUBBLING mouseover/mouseout pair — dispatching raw mouseenter/leave (they
* do not bubble) never reaches React's handlers.
*/
/** Pointer hover. React 18 synthesizes onMouseEnter/onMouseLeave from the
* BUBBLING mouseover/mouseout pair — dispatching raw mouseenter/leave (they do not bubble) never reaches React's handlers. */
export async function hover(el: Element): Promise<void> {
await act(async () => {
el.dispatchEvent(new MouseEvent('mouseover', { bubbles: true }))
+2 -4
View File
@@ -232,8 +232,7 @@ describe('makeContentFetcher — the per-session targeted read', () => {
test('a hostile or shapeless declared face leaves the slot unset (issue #42)', () => {
// The traced proxy can throw on the property READ itself, or serve a
// null / primitive / verb-less / hostile-verb face — the declared
// callback contains all of it, the slot stays unset, and the fetcher
// degrades instead of taking the view down.
// callback contains all of it, the slot stays unset, and the fetcher degrades instead of taking the view down.
const remotes = [
{ get session() { throw new Error('traced read failed') } },
{ session: null },
@@ -259,8 +258,7 @@ describe('makeContentFetcher — the per-session targeted read', () => {
test('the declared inject tolerates a shapeless remote service', () => {
const ctx = new TestClientCtx()
// Both names exist but the facade bears no session namespace: the
// callback fires and leaves the slot unset, never throwing.
// Both names exist but the facade bears no session namespace: the callback fires and leaves the slot unset, never throwing.
ctx.setService('remote', {})
ctx.setService('remote.session', {})
watchHistoryFaces(asClientCtx(ctx))
+1 -2
View File
@@ -1,6 +1,5 @@
// Dictionaries (src/client/i18n.ts): both locales carry exactly the same
// keys, every value is a non-empty string, and interpolation placeholders
// agree between locales for every parametrized key.
// keys, every value is a non-empty string, and interpolation placeholders agree between locales for every parametrized key.
import assert from 'node:assert/strict'
import { describe, test } from 'vitest'
+1 -2
View File
@@ -54,8 +54,7 @@ describe('ContextIcon', () => {
// complement mask that each eroding shift is painted through.
assert.equal(queryAll<SVGPathElement>(m.container, 'path').length, 10)
// The inset itself: a white flood minus one black shift per axis
// direction, 17 units each — the source's 98-unit bars less the harness
// weight's 64 makes for two 17s.
// direction, 17 units each — the source's 98-unit bars less the harness weight's 64 makes for two 17s.
const [hole, thin] = queryAll<SVGMaskElement>(m.container, 'mask')
assert.match(hole.id, /-hole$/)
assert.match(thin.id, /-thin$/)
+1 -2
View File
@@ -1,7 +1,6 @@
// Client entry (src/client/index.ts): the plugin's apply() wiring asserted
// through the faithful harness-context seams — dictionaries, slots, the
// /context trigger source, and the deferred configForms inject — plus real
// renders of the registered components.
// /context trigger source, and the deferred configForms inject — plus real renders of the registered components.
import { createElement as h, type ReactElement } from 'react'
import assert from 'node:assert/strict'
+1 -2
View File
@@ -113,8 +113,7 @@ describe('watchInsightPage', () => {
})
test('a slots face returning no disposer is tolerated across flips and dispose', () => {
// A minimal face: inject/register land nothing unwindable (the harness
// contract allows either to return no handle).
// A minimal face: inject/register land nothing unwindable (the harness contract allows either to return no handle).
const bare = {
slots: {
inject: (_name: string, cb: () => unknown) => { cb(); return undefined },
+1 -2
View File
@@ -1,7 +1,6 @@
// Plugin self-metadata (src/client/meta.ts): the build-time define fallbacks
// and the define-armed variants. Each variant sets the globals BEFORE its own
// dynamic import (vi.resetModules clears the module cache so the constants
// are re-evaluated), then restores the globals.
// dynamic import (vi.resetModules clears the module cache so the constants are re-evaluated), then restores the globals.
import assert from 'node:assert/strict'
import { afterEach, describe, test, vi } from 'vitest'
+1 -2
View File
@@ -117,8 +117,7 @@ describe('the price store', () => {
assert.deepEqual(getModelPricesSnap(), { book: null, failed: false })
const seen: ModelPricesSnap[] = []
const un = subscribeModelPrices(() => seen.push(getModelPricesSnap()))
// A second subscription in the same tick finds the fetch already in
// flight and never re-kicks.
// A second subscription in the same tick finds the fetch already in flight and never re-kicks.
const un2 = subscribeModelPrices(() => {})
await settle()
assert.equal(calls, 1)
+2 -4
View File
@@ -1,6 +1,5 @@
// The Context Dashboard's data layer (src/client/overview.ts): the hostile-
// snapshot row join, filters, sorts, aggregations, and relative time — every
// guard branch with hostile fixtures.
// snapshot row join, filters, sorts, aggregations, and relative time — every guard branch with hostile fixtures.
import assert from 'node:assert/strict'
import { describe, test, vi } from 'vitest'
@@ -149,8 +148,7 @@ describe('rowsOfSnapshot', () => {
const rows = rowsOfSnapshot(snap, { archivedSessionIds: ['b', 7, null] })
assert.ok(rows !== null)
assert.deepEqual(rows.map(r => r.id), ['a', 'c'], 'archived ids drop; non-string entries archive nothing')
// Fail-open shapes: an absent seat, a bare record, a malformed set, and a
// throwing accessor all keep the full list.
// Fail-open shapes: an absent seat, a bare record, a malformed set, and a throwing accessor all keep the full list.
assert.equal(rowsOfSnapshot(snap)?.length, 3)
assert.equal(rowsOfSnapshot(snap, null)?.length, 3)
assert.equal(rowsOfSnapshot(snap, 7)?.length, 3)
+1 -2
View File
@@ -1,7 +1,6 @@
// Placement gating (src/client/placement.ts): the mounts follow the
// `defaultPlacement` preference — both, the tab only, or the sidebar only —
// a flip disposes exactly what the new value drops, and the watcher's own
// disposer unwinds everything and detaches the store.
// a flip disposes exactly what the new value drops, and the watcher's own disposer unwinds everything and detaches the store.
import assert from 'node:assert/strict'
import { describe, test } from 'vitest'
+1 -2
View File
@@ -32,8 +32,7 @@ describe('resolveIcon', () => {
})
test('the module-level exports resolve against the pinned generation', () => {
// The devDep carries the supported vocabulary, so these resolved at
// module load.
// The devDep carries the supported vocabulary, so these resolved at module load.
for (const icon of [IconBranch, IconPlus, IconCheck, IconCopy, IconClose, IconSettings, IconChevronDown]) {
assert.equal(typeof icon, 'function', 'every seam resolves to a component')
}
+1 -2
View File
@@ -1,7 +1,6 @@
// revealScroll (src/client/revealScroll.ts) — the jump's landing reveal,
// driven against real jsdom DOM: the nearest ancestor that ACTUALLY scrolls
// (overflowing style alone is not enough) takes the rect-delta scroll, the
// walk stops at body/null, hostile chains never throw.
// (overflowing style alone is not enough) takes the rect-delta scroll, the walk stops at body/null, hostile chains never throw.
import assert from 'node:assert/strict'
import { describe, test } from 'vitest'
+1 -2
View File
@@ -339,8 +339,7 @@ describe('timelineOf', () => {
test('a slim head carrying the (empty) collections passes through by reference, markers intact', () => {
// The host's head builder emits the four collections as empty lists, so
// the split generation rides the cheap pass-through path like any
// well-formed value — the markers/counts survive untouched.
// the split generation rides the cheap pass-through path like any well-formed value — the markers/counts survive untouched.
const head = {
ok: true,
current,
+7 -14
View File
@@ -5,10 +5,8 @@ import assert from 'node:assert/strict'
import { describe, test } from 'vitest'
import { createContextSettings, type SettingsScopeLike } from '../../src/client/settings'
/**
* A faithful in-memory settings form (the harness configForms.get contract:
* getSnapshot/subscribe/set), not a mock of plugin code.
*/
/** A faithful in-memory settings form (the harness configForms.get contract:
* getSnapshot/subscribe/set), not a mock of plugin code. */
class TestSettingsScope implements SettingsScopeLike {
private snapshot: { status: string; value: unknown; writable: boolean }
private readonly listeners = new Set<() => void>()
@@ -145,8 +143,7 @@ describe('set', () => {
test('a rejected placement write degrades to all when the scope carries no valid value', async () => {
// Fail open: the unpersisted echo must not keep hiding an entry until
// the next reload — with nothing valid in the scope's truth, fall back
// to `all`.
// the next reload — with nothing valid in the scope's truth, fall back to `all`.
const s = createContextSettings()
const scope = new TestSettingsScope({ status: 'ready', value: {}, writable: true })
s.attach(scope)
@@ -158,8 +155,7 @@ describe('set', () => {
})
test('a rejected placement write rolls back to the scope\'s valid truth', async () => {
// The scope truth itself is a valid placement: the rollback restores it,
// no forced degrade.
// The scope truth itself is a valid placement: the rollback restores it, no forced degrade.
const s = createContextSettings()
const scope = new TestSettingsScope({ status: 'ready', value: { defaultPlacement: 'sidebar' }, writable: true })
s.attach(scope)
@@ -171,8 +167,7 @@ describe('set', () => {
test('a rejected insights-entry write degrades to show when the scope carries no valid value', async () => {
// Fail open: the unpersisted echo must not keep the panel's entry hidden
// until the next reload — with nothing valid in the scope's truth, fall
// back to `show`.
// until the next reload — with nothing valid in the scope's truth, fall back to `show`.
const s = createContextSettings()
const scope = new TestSettingsScope({ status: 'ready', value: {}, writable: true })
s.attach(scope)
@@ -277,8 +272,7 @@ describe('attach', () => {
})
test('an invalid scope placement degrades to all instead of keeping the current one', () => {
// Fail open on the read path too: a value the plugin cannot understand
// must not strand a previously chosen placement.
// Fail open on the read path too: a value the plugin cannot understand must not strand a previously chosen placement.
const s = createContextSettings()
s.set('defaultPlacement', 'tab')
assert.equal(s.defaultPlacement(), 'tab')
@@ -288,8 +282,7 @@ describe('attach', () => {
})
test('an invalid scope insights entry degrades to show instead of keeping the current one', () => {
// Fail open on the read path too: a config problem must not leave the
// panel's entry hidden.
// Fail open on the read path too: a config problem must not leave the panel's entry hidden.
const s = createContextSettings()
s.set('insightsEntry', 'hide')
assert.equal(s.insightsEntry(), 'hide')
+1 -2
View File
@@ -1,8 +1,7 @@
// openPluginSettings (src/client/settingsJump.ts): the guarded DOM jump to
// the plugin's preferences on the Plugins main panel — panel entry first,
// the bundle card's open control after the page settles — plus the hostile
// degradations (missing chrome, throwing elements, a card that never lists).
// jsdom supplies the real document.
// degradations (missing chrome, throwing elements, a card that never lists). jsdom supplies the real document.
import assert from 'node:assert/strict'
import { afterEach, describe, test } from 'vitest'
+1 -2
View File
@@ -1,7 +1,6 @@
// The right Sidebar's Context tab (src/client/sidebar.ts): the OPTIONAL
// three-stage registration — tab type into `sidebarRightTabs`, body into the
// keyed `sidebar.right.pane.tab` seat, chip title into
// `sidebar.right.pane.tab.title` — and its silent degrade on every harness
// keyed `sidebar.right.pane.tab` seat, chip title into `sidebar.right.pane.tab.title` — and its silent degrade on every harness
// that serves no right Sidebar (the older supported lines).
import { createElement as h, type ReactElement } from 'react'
+4 -8
View File
@@ -1,8 +1,7 @@
// The timeline source (src/client/timelineSource.ts): the detail payload
// narrow, the Connection-RPC fetcher, the per-session detail store machine
// (debounce / single-flight / latest-wins / refold / retry / backoff), and
// the useTimelineSource hook merging both wire generations into the value
// the cards render.
// the useTimelineSource hook merging both wire generations into the value the cards render.
import { act, createElement as h, type ReactElement } from 'react'
import assert from 'node:assert/strict'
@@ -735,8 +734,7 @@ describe('useTimelineSource', () => {
return { body: { ok: true, value: detail(2, { head: slimHead(2) }) } }
})
const m = await mount(h(SourceProbe, { sessionId: 's1', value: undefined }))
// The first paint waits on the cold read — then the read's own slim head
// stands in for the never-pushed projection value.
// The first paint waits on the cold read — then the read's own slim head stands in for the never-pushed projection value.
assert.equal(probeRead(m.container, 'state'), 'loading')
await until(() => probeRead(m.container, 'state') === 'ready', 'the cold read never landed')
assert.equal(probeRead(m.container, 'model'), 'm', 'the read head renders')
@@ -763,8 +761,7 @@ describe('useTimelineSource', () => {
const m = await mount(h(PushProbe, null))
await until(() => probeRead(m.container, 'state') === 'ready', 'the cold read never landed')
assert.equal(probeRead(m.container, 'model'), 'cold-m')
// The pushed head outruns the cold read's revision: same per-session
// store, one trailing refetch, the pushed value renders.
// The pushed head outruns the cold read's revision: same per-session store, one trailing refetch, the pushed value renders.
servedRev = 2
pushed = slimHead(2, { model: 'pushed-m' })
await m.update(h(PushProbe, null))
@@ -853,8 +850,7 @@ describe('useTimelineSource', () => {
assert.equal(detailStoreOf('s1').getSnapshot().detail?.rev, 3)
served = 4
await m.update(h(SourceProbe, { sessionId: 's1', value: slimHead(4) }))
// The probe's rev cell reads the HEAD's marker (4 as pushed); the store's
// served detail proves the refetch.
// The probe's rev cell reads the HEAD's marker (4 as pushed); the store's served detail proves the refetch.
assert.equal(probeRead(m.container, 'rev'), '4')
await until(() => detailStoreOf('s1').getSnapshot().detail?.rev === 4, 'the refetch never landed')
await m.unmount()
+2 -4
View File
@@ -1,6 +1,5 @@
// viewFocus (src/client/viewFocus.ts) — the chat→Context jump relay, the
// sidebar-tab opener, and the conversation-tab activation, driven against a
// real jsdom tab bar.
// sidebar-tab opener, and the conversation-tab activation, driven against a real jsdom tab bar.
import assert from 'node:assert/strict'
import { describe, test } from 'vitest'
@@ -42,8 +41,7 @@ describe('context focus subscription', () => {
test('a listener unsubscribing another mid-notify skips it (later records do not re-run it)', () => {
let late = 0
let offLate: () => void = () => {}
// Insertion order matters: the remover runs first and detaches the victim
// before Set iteration reaches it.
// Insertion order matters: the remover runs first and detaches the victim before Set iteration reaches it.
const offRemover = subscribeContextFocus(() => { offLate() })
offLate = subscribeContextFocus(() => { late++ })
requestContextFocus('sv-mid', 1)
+2 -5
View File
@@ -4,11 +4,9 @@
// data-plugin style tags, platform-module requires) only exists after tsdown
// runs. Skipped cleanly when lib/ is absent (run `pnpm run build` first);
// the release workflow builds before `pnpm test`, so it always runs there.
//
// Everything here runs against the REAL bundle in its own JSDOM with REAL
// React and the REAL ui-primitives — only the harness services
// (locale/slots/effects) are in-memory implementations of their documented
// contracts.
// (locale/slots/effects) are in-memory implementations of their documented contracts.
import assert from 'node:assert/strict'
import { readFileSync } from 'node:fs'
@@ -48,8 +46,7 @@ describe.skipIf(staging.artifactsMissing())('bundle smoke — the built lib/clie
// must require nothing else (the tsdown purity gate pins this too). The
// primitives dist ships CSS modules Node cannot parse — the bundle only
// references its components as React leaves, so inert stand-ins play the
// shell's role here (their real rendering is covered by the vitest
// jsdom lane).
// shell's role here (their real rendering is covered by the vitest jsdom lane).
const inert = (name: string) => {
const C = (): null => null
;(C as { displayName?: string }).displayName = name
+9 -19
View File
@@ -6,15 +6,11 @@
// on the cordis release that line vendors, the tag's own settings-namespace
// pattern, its durable-event vocabulary, its platform module table, and its
// client seam sources (slots, seats, image/history faces, markdown chrome).
// A failing probe names the SEAM — the connection point to re-fit or
// refactor — not just "it broke somewhere".
//
// A failing probe names the SEAM — the connection point to re-fit or refactor — not just "it broke somewhere".
// Preconditions (skipped cleanly when absent; the release workflow fetches
// the baseline tags before `pnpm test`, so it always runs there):
// - a dsh checkout with the baseline tags (env DSH_REPO, default
// ~/dev/deepseek-harness),
// - the built plugin (`pnpm run build` first — the matrix exercises the
// BUILT artifacts, lib/index.js + lib/client.js).
// - a dsh checkout with the baseline tags (env DSH_REPO, default ~/dev/deepseek-harness),
// - the built plugin (`pnpm run build` first — the matrix exercises the BUILT artifacts, lib/index.js + lib/client.js).
import assert from 'node:assert/strict'
import { beforeAll, describe, test } from 'vitest'
@@ -28,8 +24,7 @@ if (reasons.length > 0) {
// The always-runnable part (no checkout needed): every specifier the built
// bundle requires at runtime must be seeded by EACH baseline's platform
// module table — a require the shell cannot answer is a guaranteed boot
// crash on that generation.
// module table — a require the shell cannot answer is a guaranteed boot crash on that generation.
describe('compat matrix — the per-baseline fold vocabularies', () => {
test('their union covers every event family the fold switches on', () => {
const union = new Set(BASELINES.flatMap(baseline => baseline.foldEventTypes))
@@ -54,8 +49,7 @@ describe.skipIf(reasons.length > 0)('compat matrix — real dsh sources per base
describe.each(BASELINES)('$id (tag $tag, cordis $cordis)', (baseline) => {
let report: staging.DriverReport
// 60s: the driver's own spawn budget. A cold stage dir (fresh clone,
// cache miss, dependency re-pin) runs a real npm install inside the hook,
// which the 10s default ceiling cannot cover.
// cache miss, dependency re-pin) runs a real npm install inside the hook, which the 10s default ceiling cannot cover.
beforeAll(() => {
report = staging.runDriver(baseline)
}, 60_000)
@@ -87,8 +81,7 @@ describe.skipIf(reasons.length > 0)('compat matrix — real dsh sources per base
test('host: hostile gateway usage keeps the served view schema-valid (issue #44)', () => {
// Negative uncached input (cached_tokens > prompt_tokens), fractional
// counts, string buckets, and a hostile NaN must all survive the REAL
// registry's strict wire parse — billed from sanitized buckets, never
// frozen.
// registry's strict wire parse — billed from sanitized buckets, never frozen.
assert.equal(report.hostileSnapshotOk, true)
assert.equal(report.hostilePrompt, 161)
assert.equal(report.hostileCacheRead, 151)
@@ -120,8 +113,7 @@ describe.skipIf(reasons.length > 0)('compat matrix — real dsh sources per base
// The balance route resolves the provider's settings row off the
// Config-form `describe()` projection (the row matched by its own
// apiKeyEnv-bearing section shape); the retired `get(ns)` section read
// is asserted absent, so a moved seam names itself instead of silently
// hiding the capsule.
// is asserted absent, so a moved seam names itself instead of silently hiding the capsule.
const balance = baseline.balance
assert.equal(
staging.dshHasString(baseline.tag, balance.settingsGetNeedle, balance.settingsFile),
@@ -153,8 +145,7 @@ describe.skipIf(reasons.length > 0)('compat matrix — real dsh sources per base
// transport it rides: the Plugins page's `plugins.bundle.config` +
// `configForms` on every supported line. Asserted BOTH ways (like the
// sidebar seam) so a moved seam cannot read as "unsupported here" — a
// slot the generation does not declare simply never receives the
// registration.
// slot the generation does not declare simply never receives the registration.
const settings = baseline.settings
assert.equal(
settings.cardSlotFiles.some(pattern =>
@@ -214,8 +205,7 @@ describe.skipIf(reasons.length > 0)('compat matrix — real dsh sources per base
test('client: the session-jump seam of this generation (issue #90)', () => {
// The view owner's navigation verb (the sidebar row click's own), with
// the signature this line spells; and the retired sessions-service
// `open(id)` selection verb, probed staying gone.
// the signature this line spells; and the retired sessions-service `open(id)` selection verb, probed staying gone.
const nav = baseline.client.sessionNav
assert.equal(
staging.dshHasString(baseline.tag, nav.workspaceNeedle, nav.workspaceFile),
+11 -27
View File
@@ -1,12 +1,9 @@
// Staging + probe helpers for the compat matrix (matrix.spec.ts): stage the
// REAL harness sources at a baseline tag, boot the plugin's BUILT host entry
// on the tag's real registry + vendored cordis, and read the tag's client
// seams straight from its sources.
//
// on the tag's real registry + vendored cordis, and read the tag's client seams straight from its sources.
// The registry probe runs as a small spawned driver INSIDE the staging dir,
// so the bare `@deepseek-ai/cordis` import resolves to the cordis release
// that harness line vendors (not this repo's) — one process per baseline,
// reported as one JSON line.
// that harness line vendors (not this repo's) — one process per baseline, reported as one JSON line.
import { spawnSync } from 'node:child_process'
import { existsSync, mkdirSync, readFileSync, writeFileSync } from 'node:fs'
@@ -106,12 +103,9 @@ export interface DriverReport {
poisonedSnapshotThrows: boolean
}
/**
* The staged host driver: runs INSIDE the baseline's staging dir, applies the
/** The staged host driver: runs INSIDE the baseline's staging dir, applies the
* plugin's real host entry into the tag's real registry, drives a canonical
* log through the registry's own snapshot / checkpoint / restore paths, and
* reports as one JSON line.
*/
* log through the registry's own snapshot / checkpoint / restore paths, and reports as one JSON line. */
function driverSource(pluginUrl: string, developerMessages: boolean): string {
return `
import { Context } from '@deepseek-ai/cordis'
@@ -188,8 +182,7 @@ if (${developerMessages}) {
// A mis-accounting gateway's figures (negative uncached input from
// cached_tokens > prompt_tokens, fractional counts, string buckets, a NaN a
// hostile direct-drive could supply) ride the durable log legally; the
// plugin's served values must stay schema-valid through the registry's own
// strict parse on every later delivery.
// plugin's served values must stay schema-valid through the registry's own strict parse on every later delivery.
const hostile = [
ev(12, 'step/start'),
ev(13, 'assistant/message', { turn: 1, step: 2, message: { content: [{ type: 'text', text: 'h1' }] }, usage: { inputTokens: -80, cacheReadTokens: 150.7, cacheWriteTokens: '10', outputTokens: 22.4 } }),
@@ -294,26 +287,20 @@ export function bundleRequires(): string[] {
return [...new Set([...source.matchAll(/require\("([^"]+)"\)/g)].map(m => m[1]))]
}
/**
* The slot registrations the client half mounts on EVERY baseline (identical
/** The slot registrations the client half mounts on EVERY baseline (identical
* spellings in each generation's sources). The preferences card seat and the
* settings transport are asserted separately, per generation — the matrix
* spec pins them to the baseline's `settings` seam (`plugins.bundle.config` +
* `configForms` on every supported line).
*/
* spec pins them to the baseline's `settings` seam (`plugins.bundle.config` + `configForms` on every supported line). */
export const SLOT_SEAMS = [
'conversation.view',
'conversation.chat.assistant-actions',
'conversation.input.overlay',
] as const
/**
* The ui-primitives icon seams the client bundle renders: every supported
/** The ui-primitives icon seams the client bundle renders: every supported
* line ships the `…OutlineRegular`/`…OutlineMedium` vocabulary, and a missing
* name is `undefined` at the browser's runtime — React error #130.
* `src/client/primitives.ts` resolves each by name; every one must exist per
* tag.
*/
* `src/client/primitives.ts` resolves each by name; every one must exist per tag. */
export const ICON_SEAMS = [
'IconBranchOutlineRegular',
'IconPlusOutlineRegular',
@@ -326,13 +313,10 @@ export const ICON_SEAMS = [
'IconSearchOutlineRegular',
] as const
/**
* The event families the host fold switches on (src/host/fold.ts) — the UNION
/** The event families the host fold switches on (src/host/fold.ts) — the UNION
* over every supported generation. The per-baseline probe asserts the
* baseline's own `foldEventTypes` subset, and a matrix test asserts this
* union equals the baselines' union — a fold case added without a baseline
* list fails loudly instead of going unprobed.
*/
* union equals the baselines' union — a fold case added without a baseline list fails loudly instead of going unprobed. */
export const FOLD_EVENT_TYPES = [
'request/header', 'request/context', 'step/start', 'step/end',
'user/message', 'developer/message', 'tool/call', 'tool/result', 'assistant/message',
+3 -7
View File
@@ -20,12 +20,9 @@ const specFile = path.normalize(fileURLToPath(import.meta.url))
* registry); reading it yields the wrapped underlying service instance. */
const CORDIS_ORIGINAL = Symbol.for('cordis.original')
/**
* A tracked tools service shaped like dsh's ToolRuntime: a real Service
/** A tracked tools service shaped like dsh's ToolRuntime: a real Service
* subclass whose `register` is a PROTOTYPE method — the shape that makes
* cordis hand out a fresh traced proxy (closing over the reader's context)
* on every `ctx.tools` property read.
*/
* cordis hand out a fresh traced proxy (closing over the reader's context) on every `ctx.tools` property read. */
class TrackedTools extends Service {
layers = { global: { tools: new Map() } }
constructor(ctx: Context) { super(ctx, 'tools') }
@@ -211,8 +208,7 @@ describe('createToolAttribution', () => {
const first = createToolAttribution(app)
void first
// The second hook incarnation (a reload) peels the first wrapper back to
// the original: a registration records into the NEW hook only, and the
// original register still runs exactly once.
// the original: a registration records into the NEW hook only, and the original register still runs exactly once.
const second = createToolAttribution(app)
await app.plugin({
name: 'provider-b',
+5 -10
View File
@@ -4,8 +4,7 @@
// route hits no-op), the deferred inject's service gating, the per-session
// skip ladder (malformed, invisible, live, already fully served), the
// cold-read → coldSnapshot fold path, per-session failure isolation with
// the migration-refusal accounting (debug detail, one summary info), and
// abort-on-dispose.
// the migration-refusal accounting (debug detail, one summary info), and abort-on-dispose.
import assert from 'node:assert/strict'
import { describe, test } from 'vitest'
@@ -48,8 +47,7 @@ interface LogLine {
/** Capture the pass's log lines through the logger service's exporter sink. */
function captureLogs(ctx: Context): LogLine[] {
const lines: LogLine[] = []
// Lift every source's threshold to debug: the default exporter drops
// warn/debug records before they reach the sink.
// Lift every source's threshold to debug: the default exporter drops warn/debug records before they reach the sink.
ctx.logger.exporter({
levels: { root: 3, 'dsh-context': 3 },
export(message) {
@@ -59,12 +57,10 @@ function captureLogs(ctx: Context): LogLine[] {
return lines
}
/**
* One format-refusal face of the persistence seam. The format edge throws
/** One format-refusal face of the persistence seam. The format edge throws
* `SessionFormatUnsupportedMigrationError`, but the seam translates it — with
* the "source vN artifact remains unchanged (raw log: …)" suffix — into
* `SessionFormatUnsupportedError` before the error escapes (issue #75): the
* pass must classify BOTH faces as the same refusal.
* `SessionFormatUnsupportedError` before the error escapes (issue #75): the pass must classify BOTH faces as the same refusal.
*/
function unsupportedFormatError(name: string): Error {
const error = new Error(
@@ -613,8 +609,7 @@ describe('watchActivityBackfill', () => {
ctx.provide('connection', connectionOf(route))
const dispose = watchActivityBackfill(ctx)
await trigger(route)
// The request landed while only the route half existed; the faces land
// now and the pass must still run.
// The request landed while only the route half existed; the faces land now and the pass must still run.
ctx.provide('sessionQuery', { listSessions: async () => state.listed })
ctx.provide('sessionProjectionCache', {
cachedSnapshot: () => undefined,
+2 -4
View File
@@ -19,11 +19,9 @@ interface CtxSpec {
credentials?: unknown
}
/**
* A minimal host ctx double with cordis inject semantics: the callback runs
/** A minimal host ctx double with cordis inject semantics: the callback runs
* once its dependency list completes (here: immediately when the services
* map has them, never otherwise), and its returned disposer is collected.
*/
* map has them, never otherwise), and its returned disposer is collected. */
function ctxOf(spec: CtxSpec): { ctx: Context; captured: { path?: string; fetch?: RouteFetch }; disposers: (() => void)[] } {
const captured: { path?: string; fetch?: RouteFetch } = {}
const disposers: (() => void)[] = []
+1 -2
View File
@@ -70,8 +70,7 @@ describe('makeColdReadGate', () => {
ran++
return 'b'
})
// Let the first read actually START (its check passed), then let pressure
// rise while the second read waits behind it.
// Let the first read actually START (its check passed), then let pressure rise while the second read waits behind it.
await new Promise(resolve => setTimeout(resolve, 10))
assert.ok(started, 'the front read started under the low reading')
reading = HIGH
+5 -11
View File
@@ -7,9 +7,7 @@
// generation (issues #26/#8 — the tab stuck "Reading the session log…"), a
// state that fails the projection cache's plain-JSON write gate (issues
// #5-#7, #27-#30 — session creation/titles broke host-wide), a stale-row
// restore that must seed through stateSchema, and an init that must tolerate
// the registry's header argument.
//
// restore that must seed through stateSchema, and an init that must tolerate the registry's header argument.
// The real-code complement — the ACTUAL dsh registry sources per tag — runs
// in the `compat` vitest project (tests/compat/matrix.spec.ts).
@@ -95,8 +93,7 @@ for (const [index, baseline] of BASELINES.entries()) {
test('the split generation (slim head) rides the same registry contract', () => {
// The slim head is the wire value when the detail channel is live
// (host/detail.ts): the registry drives, caches, restores, and gates it
// identically — only the served value differs.
// (host/detail.ts): the registry drives, caches, restores, and gates it identically — only the served value differs.
const driver = new RegistryDriver(baseline)
driver.register(createContextTimelineDefinition({}, () => true))
driver.register(createContextHeadersDefinition())
@@ -120,8 +117,7 @@ for (const [index, baseline] of BASELINES.entries()) {
assert.equal(timeline.counts?.steps, 2, 'the counters travel on the head')
assert.equal(timeline.nodes.length, 0, 'the collections stay off the wire')
assert.equal(timeline.requests.length, 0)
// The cache contract is unchanged: plain-JSON rows, and a restore that
// refolds to the live cut.
// The cache contract is unchanged: plain-JSON rows, and a restore that refolds to the live cut.
assert.ok(driver.checkpointJson(session) !== undefined)
const cold = driver.restore({}, log, 0, session.header)
assert.deepEqual(cold.values, driver.snapshot(session).values)
@@ -193,8 +189,7 @@ for (const [index, baseline] of BASELINES.entries()) {
// carries a raw nonconforming gateway figure, while the durable log
// holds every committed step. The bump makes the stale row unusable, so
// the cold read refolds from init over the FULL log — the steps the
// frozen feed never showed come back, billed with sanitized figures and
// passing the real wire schema on the way out.
// frozen feed never showed come back, billed with sanitized figures and passing the real wire schema on the way out.
const log = [
...fullLog(),
assistantMessage(13, { turn: 1, step: 2, usage: { inputTokens: -80, cacheReadTokens: 150.7, outputTokens: 22.4 } }),
@@ -223,8 +218,7 @@ for (const [index, baseline] of BASELINES.entries()) {
const { driver, session } = bootSession(index, log)
assert.equal(driver.viewCheckpoint(stale).contextTimeline, undefined)
// The cold read refolds from init over the full durable log; a schema
// violation anywhere would throw right here (restore parses each wire
// value through the unit's real viewSchema).
// violation anywhere would throw right here (restore parses each wire value through the unit's real viewSchema).
const restored = driver.restore(stale, log, 0, session.header)
const timeline = restored.values.contextTimeline as { requests: { seq: number; prompt?: number; cacheRead?: number; output?: number }[] }
// Every step is tracked again — the hostile ones included…
+7 -18
View File
@@ -1,11 +1,8 @@
// A per-baseline driver over the session-projection registry's DOCUMENTED
// semantics — the rules the REAL dsh registries enforce, mirrored from the
// source at each baseline tag so the always-on suite pins the plugin's units
// against every supported registry generation:
//
// source at each baseline tag so the always-on suite pins the plugin's units against every supported registry generation:
// dsh-v0.1.7-rc.2 packages/session/session-projection/src/index.ts
// dsh-v0.2.0-rc.2 packages/session/session-projection/src/index.ts
//
// The real-code matrix (tests/compat/matrix.spec.ts) runs the ACTUAL registry
// sources per tag; this driver exists so `pnpm test` alone still detects a
// definition that drifts from any supported generation's rules (a missing
@@ -61,9 +58,7 @@ export interface CheckpointRow {
export type Checkpoint = Record<string, CheckpointRow>
/**
* The registry's register-time rules: a non-negative safe integer
* `stateVersion`, and a shared key must agree on the version.
/** The registry's register-time rules: a non-negative safe integer `stateVersion`, and a shared key must agree on the version.
*/
export class RegistryViolationError extends Error {}
@@ -89,8 +84,7 @@ export class RegistryDriver {
const def: ErasedDefinition = {
key: definition.key,
stateSchema: definition.stateSchema,
// The registry calls init(header, inheritedEventCount); the plugin's
// zero-argument init observes neither argument.
// The registry calls init(header, inheritedEventCount); the plugin's zero-argument init observes neither argument.
init: (header, inheritedEventCount) => definition.init(header, inheritedEventCount),
apply: (state, event) => definition.apply(state, event),
wire,
@@ -171,13 +165,10 @@ export class RegistryDriver {
return rows
}
/**
* The projection cache's WRITE precondition (session-projection-cache
/** The projection cache's WRITE precondition (session-projection-cache
* src/index.ts): every row must be losslessly JSON-serializable or the
* whole write fails with a TypeError — one undefined-valued property in
* one unit's state once broke every session on the host (issues #5-#7,
* #27-#30). Returns the detached JSON image.
*/
* one unit's state once broke every session on the host (issues #5-#7, #27-#30). Returns the detached JSON image. */
checkpointJson(session: SessionLike): Record<string, unknown> | undefined {
return snapshotJson(this.checkpoint(session)) as Record<string, unknown> | undefined
}
@@ -211,11 +202,9 @@ export class RegistryDriver {
return floor === undefined ? undefined : Math.max(floor - 1, 0)
}
/**
* Cold read: seed usable rows through stateSchema.parse, refold the rest.
/** Cold read: seed usable rows through stateSchema.parse, refold the rest.
* `inheritedEventCount` is the fork-inherited prefix length the registry
* hands to unit initialization (0 for a plain session).
*/
* hands to unit initialization (0 for a plain session). */
restore(checkpoint: Checkpoint, events: { seq: number }[], baseSeq: number, header: unknown, inheritedEventCount = 0): ProjectionSnapshot {
const endSeq = events.at(-1)?.seq ?? baseSeq - 1
const values: Record<string, unknown> = {}
+1 -2
View File
@@ -107,8 +107,7 @@ describe('volatileField', () => {
test('returns the field unchanged on a schemastery without the modifier (the older lines)', () => {
const plain = z.union(['a', 'b']).default('a')
// A schema-shaped value with no `volatile` member at all — the shape the
// older lines' schemastery presents.
// A schema-shaped value with no `volatile` member at all — the shape the older lines' schemastery presents.
const legacy = { meta: plain.meta } as typeof plain
assert.equal(volatileField(legacy), legacy)
assert.equal(legacy.meta?.volatile, undefined)
+3 -6
View File
@@ -2,8 +2,7 @@
// on the connection/sessions faces (load-order independent), the gate's live
// flip on unload, and the route's typed outcomes — the detail payload off the
// live fold state, the typed `null` for a gone session or an absent unit, and
// the failure envelope for bad payloads and hostile reads (the route must
// never throw into the transport).
// the failure envelope for bad payloads and hostile reads (the route must never throw into the transport).
import assert from 'node:assert/strict'
import { describe, test } from 'vitest'
@@ -25,11 +24,9 @@ interface CtxSpec {
stateOf?: (session: unknown, key: string) => unknown
}
/**
* A minimal host ctx double with cordis inject semantics: the callback runs
/** A minimal host ctx double with cordis inject semantics: the callback runs
* once its dependency list completes (here: immediately when the services
* map has them, never otherwise), and its returned disposer is collected.
*/
* map has them, never otherwise), and its returned disposer is collected. */
function ctxOf(spec: CtxSpec): { ctx: Context; captured: { path?: string; fetch?: RouteFetch }; disposers: (() => void)[] } {
const captured: { path?: string; fetch?: RouteFetch } = {}
const disposers: (() => void)[] = []
+1 -2
View File
@@ -1,8 +1,7 @@
// 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.
// 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'
+1 -2
View File
@@ -1,3 +1,2 @@
// Typing for the throwaway plain-JS fixture (the runtime module mirrors this
// exactly); keeps tsc happy without allowJs.
// Typing for the throwaway plain-JS fixture (the runtime module mirrors this exactly); keeps tsc happy without allowJs.
export function registerBlind(tools: { register(def?: unknown): () => void }): () => void
+1 -2
View File
@@ -1,3 +1,2 @@
// Typing for the throwaway plain-JS fixture (the runtime module mirrors this
// exactly); keeps tsc happy without allowJs.
// Typing for the throwaway plain-JS fixture (the runtime module mirrors this exactly); keeps tsc happy without allowJs.
export function registerTool(tools: { register(def?: unknown): () => void }): void
+3 -6
View File
@@ -216,8 +216,7 @@ describe('user/message injection records', () => {
test('a hostile form (truthy non-string) degrades to the default inject form', () => {
// The harness validates a source's `kind` but not its `form`: a durable
// number must not ride the record into the strict wire/state schemas
// (the permanent per-session freeze class).
// number must not ride the record into the strict wire/state schemas (the permanent per-session freeze class).
const { state } = driveTimeline([
userMessage(1, text('note'), { kind: 'plugin', plugin: 'dsh-x', form: 123 } as never),
])
@@ -431,8 +430,7 @@ describe('hostile provider usage (issue #44: stats must survive nonconforming fi
// against the unit's strict integer schemas with no containment: one raw
// nonconforming figure in the state fails the gate on EVERY later delivery
// and permanently freezes the session's projection feed. The fold therefore
// sanitizes each bucket before it touches the state, and the records stay
// complete (billed, not dropped).
// sanitizes each bucket before it touches the state, and the records stay complete (billed, not dropped).
test('fractional buckets round to integers', () => {
const { state } = driveTimeline([
assistantMessage(1, { usage: { inputTokens: 10.4, cacheReadTokens: 20.6, outputTokens: 3.5 } }),
@@ -776,8 +774,7 @@ describe('hostile events', () => {
test('null and primitive content blocks price and render as nothing', () => {
const blocks = [null, 7, 'x', { type: 'text', text: 'hello' }] as never
// The tool/result has NO preceding tool/call — the unpaired lookup must
// not stamp an `undefined`-valued node.tool (it would fail every
// projection-cache write for the session).
// not stamp an `undefined`-valued node.tool (it would fail every projection-cache write for the session).
const { state } = driveTimeline([
userMessage(1, blocks),
toolResult(2, { callId: 'c1', content: blocks }),
+2 -4
View File
@@ -2,8 +2,7 @@
// book at tool/result off the armed call's arguments and the result's
// presentation meta; Code-Mode sub-dispatches buffer under their run_code
// root and flush at its result with parent/program stamps; the log trims to
// maxFileOps with a coverage floor; and the detail builder serves detached
// copies. Driven through the real projection unit.
// maxFileOps with a coverage floor; and the detail builder serves detached copies. Driven through the real projection unit.
import assert from 'node:assert/strict'
import { describe, test } from 'vitest'
@@ -90,8 +89,7 @@ describe('the file-op log — call/result pairing', () => {
toolResult(2, { callId: 'c1', content: [{ type: 'text', text: 'no' }], error: true }),
toolCall(3, { callId: 'c2', name: 'read', arguments: JSON.stringify({ file_path: 'b.ts' }) }),
// A hostile envelope-only mark (the retired pre-V4 spelling): the
// admission validation forbids `data.error` without the message mark,
// so a row carrying it alone reads as non-error.
// admission validation forbids `data.error` without the message mark, so a row carrying it alone reads as non-error.
{
type: 'tool/result', seq: 4, time: 4,
data: {
+4 -8
View File
@@ -3,8 +3,7 @@
// driven through the real projection unit with tiny config bounds. The
// restored-state defenses (a projection-cache restore can carry archived
// entries without `gone` — the state schema leaves it optional) are exercised
// by feeding hand-crafted states through def.apply, exactly what a cache
// restore seeds.
// by feeding hand-crafted states through def.apply, exactly what a cache restore seeds.
import assert from 'node:assert/strict'
import { describe, test } from 'vitest'
@@ -79,8 +78,7 @@ describe('trimState request/event bounds', () => {
[1, 2, 3, 4, 5].map(i => assistantMessage(i, { turn: 1, step: i })),
{ maxKeptTurns: 10, maxRequestSteps: 3 },
)
// One turn-run: the turn trim cannot cut mid-turn, so the step backstop
// slices the newest tail.
// One turn-run: the turn trim cannot cut mid-turn, so the step backstop slices the newest tail.
assert.deepEqual(drive.state.requests.map(r => r.seq), [3, 4, 5])
})
@@ -105,8 +103,7 @@ describe('the incremental turnRuns ledger', () => {
assistantMessage(4, { turn: 2, step: 1 }),
assistantMessage(5, { turn: 2, step: 2 }),
])
// Scan semantics: the leading turn-less record forms no run; runs open at
// turn 1 (record 2) and turn 2 (record 4).
// Scan semantics: the leading turn-less record forms no run; runs open at turn 1 (record 2) and turn 2 (record 4).
assert.equal(drive.state.turnRuns, 2)
assertStatesPlainJson(drive)
})
@@ -166,8 +163,7 @@ describe('trimState restored-state defenses', () => {
const def = timelineDef({ maxArchiveNodes: 2 })
const st = createTimelineState()
st.requests.push(req(5, 1, 1))
// A restored/legacy row without `gone`: `gone ?? Infinity` keeps it above
// any oldest retained request.
// A restored/legacy row without `gone`: `gone ?? Infinity` keeps it above any oldest retained request.
st.archived.push({ seq: 1, cat: 'user', tokens: 9 })
const next = def.apply(st, planMode(10, { active: true }))
assert.deepEqual(next.archived.map(n => n.seq), [1])
+7 -14
View File
@@ -83,8 +83,7 @@ describe('spans — the step flush', () => {
// packs no reasoning chunks (the content is redacted): the first token
// lands on a tool-call fragment at 952. Metering (ttftMs) opens the
// decode window at the MARKER — anchoring the wait at the token would
// charge the 588ms the legend's own tally reports to the wait as well,
// and the rows would double-count it past 100%.
// charge the 588ms the legend's own tally reports to the wait as well, and the rows would double-count it past 100%.
const { state } = driveTimeline([
stepStart(1, { time: 0 }),
assistantMessage(2, {
@@ -118,8 +117,7 @@ describe('spans — the step flush', () => {
test('blocks stamped wholly past the message instant drop out of the tile', () => {
// Hostile stream: both markers sit beyond the assistant message's own
// instant — their claimed intervals assert decode after the message
// landed, so they paint nothing.
// instant — their claimed intervals assert decode after the message landed, so they paint nothing.
const { state } = driveTimeline([
stepStart(1, { time: 0 }),
assistantMessage(2, {
@@ -197,15 +195,13 @@ describe('spans — the step flush', () => {
{ kind: 'tools', start: 3_000, end: 3_500 },
{ kind: 'other', start: 3_500, end: 4_000 },
])
// The totals keep the TRUE per-call sums (3_500ms over 3 calls) — only the
// strip de-overlaps.
// The totals keep the TRUE per-call sums (3_500ms over 3 calls) — only the strip de-overlaps.
assert.equal(state.timing?.toolsMs, 1_000 + 1_000 + 1_500)
assert.equal(state.timing?.toolCalls, 3)
})
test('spans clamp into the step window; a window wholly outside drops', () => {
// A run whose result arrives after the step's end instant (out-of-order
// log) paints only up to the end…
// A run whose result arrives after the step's end instant (out-of-order log) paints only up to the end…
const past = driveTimeline([
stepStart(1, { time: 0 }),
toolCallAt(2, 'c1', 3_000),
@@ -272,8 +268,7 @@ describe('spans — the step flush', () => {
describe('spans — retention, wire, and schema faces', () => {
test('the collection keeps the newest tail past the 2_000-span cap', () => {
// 41 steps × 50 spans each (a 48-marker decode run): 2_050 spans — the
// cap must drop the oldest step whole. Fewer, denser steps keep the fold
// cheap enough for a CI runner's 5s test timeout.
// cap must drop the oldest step whole. Fewer, denser steps keep the fold cheap enough for a CI runner's 5s test timeout.
const events: TimelineEvent[] = []
for (let i = 0; i < 41; i++) {
const start = i * 10_000
@@ -322,8 +317,7 @@ describe('spans — retention, wire, and schema faces', () => {
])
for (const state of drive.states) drive.def.stateSchema.parse(structuredClone(state))
// A row folded before the collection existed has no `spans` key: the
// required-key schema rejects it, so the projection cache refolds it from
// the durable log (the stateVersion 22 → 23 bump).
// required-key schema rejects it, so the projection cache refolds it from the durable log (the stateVersion 22 → 23 bump).
const stale = structuredClone(drive.state) as unknown as Record<string, unknown>
delete stale.spans
assert.throws(() => drive.def.stateSchema.parse(stale))
@@ -337,8 +331,7 @@ describe('spans — retention, wire, and schema faces', () => {
toolResult(4, { callId: 'c1', content: text('ok'), time: 1_200 }),
stepEnd(5, { time: 1_400 }),
// A step left OPEN mid-accumulation: the intermediate states carry the
// armed accumulator through the same gate (driveTimeline pins the
// no-absent-members rule on every one).
// armed accumulator through the same gate (driveTimeline pins the no-absent-members rule on every one).
stepStart(6, { time: 2_000 }),
assistantMessage(7, { time: 3_000, stream: [chunkRec(2_100, { type: 'text-delta', text: 'y' })] }),
])
+6 -12
View File
@@ -2,8 +2,7 @@
// buildTimelineDetail + the detailRev ledger): the slim head carries the
// counters and the headline anchor while the detail payload serves the heavy
// collections, and the revision bumps exactly on detail-mutating folds.
// Also pins the equivalence: inline view = head + detail, so the two
// generations can never drift apart.
// Also pins the equivalence: inline view = head + detail, so the two generations can never drift apart.
import assert from 'node:assert/strict'
import { describe, test } from 'vitest'
@@ -50,8 +49,7 @@ function canonicalLog(): TimelineEvent[] {
describe('the detailRev ledger', () => {
test('bumps exactly on the detail-mutating folds of the canonical log', () => {
// Mutations: userMessage(5), assistantMessage(6), toolResult(8),
// stepEnd(9, the strip's span flush), assistantMessage(10), compaction(11),
// planMode(12) — seven in total.
// stepEnd(9, the strip's span flush), assistantMessage(10), compaction(11), planMode(12) — seven in total.
const { state } = driveTimeline(canonicalLog())
assert.equal(state.detailRev, 7)
})
@@ -59,8 +57,7 @@ describe('the detailRev ledger', () => {
test('the working-slot and envelope folds do NOT bump (nothing to refetch)', () => {
const { def, state } = driveTimeline(canonicalLog())
// step/start (arms the step slot), tool/call (arms the call ledger),
// request/context (capacity metadata), and a header with an UNCHANGED
// model all leave the detail collections as-is.
// request/context (capacity metadata), and a header with an UNCHANGED model all leave the detail collections as-is.
const follow: TimelineEvent[] = [
requestContext(13, { contextWindow: 256000 }),
header(14, { model: 'deepseek-v4-flash', provider: 'deepseek', reason: 'change' }),
@@ -97,11 +94,9 @@ describe('buildTimelineHead', () => {
assert.ok(head.current.total > 0)
// The retained records tally: one turn, two steps, the compaction event.
assert.deepEqual(head.counts, { turns: 1, steps: 2, injects: 0, compactions: 1, prunes: 0, skills: 0 })
// The whole-session tally rides the head too: the canonical log's one
// user message (injections excluded).
// The whole-session tally rides the head too: the canonical log's one user message (injections excluded).
assert.equal(head.humanInputs, 1)
// …and both its textual assistant replies (a step counts as an answer
// only when the message carried text).
// …and both its textual assistant replies (a step counts as an answer only when the message carried text).
assert.equal(head.answers, 2)
// The card preview rides the head: the canonical log's own user message.
assert.equal(head.lastUser, 'hello there')
@@ -188,8 +183,7 @@ describe('buildTimelineDetail', () => {
assert.deepEqual(detail.archive, view.archive)
assert.equal(detail.surfaceFloor, view.surfaceFloor)
assert.equal(detail.archiveFloor, view.archiveFloor)
// The slim head rides at the SAME cut: the composition the Agent
// network's cold-node ring fetch renders from.
// The slim head rides at the SAME cut: the composition the Agent network's cold-node ring fetch renders from.
assert.ok(detail.head !== undefined)
assert.deepEqual(detail.head.current, view.current)
assert.deepEqual(detail.head.counts, buildTimelineHead(state).counts)
+4 -8
View File
@@ -54,8 +54,7 @@ describe('applySurface tool/result branch', () => {
})
test('an unknown source callId pairs nothing (a replay window where the tool/call event is gone)', () => {
// Note the fold assigns the lookup miss onto the node, so this state is
// not assertPlainJson-able.
// Note the fold assigns the lookup miss onto the node, so this state is not assertPlainJson-able.
const { state } = driveTimeline([toolResult(1, { callId: 'ghost', content: text('ok') })])
assert.equal(state.surface.at(-1)?.tool, undefined)
assert.deepEqual(state.callNames, {})
@@ -72,8 +71,7 @@ describe('applySurface tool/result branch', () => {
test('a result without the source callId pairs nothing and leaves callNames untouched', () => {
// A hostile/foreign envelope: the admission validation requires the
// source callId, so a source-less row can only arrive hostile — it folds
// to an unnamed node instead of throwing.
// source callId, so a source-less row can only arrive hostile — it folds to an unnamed node instead of throwing.
const ev: TimelineEvent = {
type: 'tool/result', seq: 2, time: at(),
data: { message: { role: 'tool', toolCallId: 'keep', content: text('ok') } },
@@ -97,8 +95,7 @@ describe('applySurface tool/result branch', () => {
test('the retired envelope-only error mark no longer flags the node', () => {
// The pre-V4 spelling (an event-level `data.error` with no message mark)
// is unreachable through the supported harnesses' admission validation;
// a hostile replay carrying it degrades to an unflagged node, never a
// throw.
// a hostile replay carrying it degrades to an unflagged node, never a throw.
const ev: TimelineEvent = {
type: 'tool/result', seq: 2, time: at(),
data: {
@@ -287,8 +284,7 @@ describe('surfaceOp variants', () => {
})
test('restored state: claim without pendingShadowEventSeq skips the rewrite', () => {
// A checkpoint restore may carry the claim fields partially — def.apply
// accepts any TimelineState.
// A checkpoint restore may carry the claim fields partially — def.apply accepts any TimelineState.
const def = timelineDef({})
const base = driveTimeline([userMessage(1, text(big))]).state
const restored: TimelineState = { ...base, pendingShadowedSeqs: [1] }
+4 -8
View File
@@ -213,8 +213,7 @@ describe('timing — tool call durations', () => {
describe('timing — the generation split (reasoning / text / tool args)', () => {
test('the embedded stream tiles the generation window into the three buckets', () => {
// step start 0, first marker 200 (10ms BEFORE the first token 210 — the
// wait/generation boundary opens there), blocks reasoning→text→tool-call,
// message at 2000.
// wait/generation boundary opens there), blocks reasoning→text→tool-call, message at 2000.
const stream = [
chunkRec(200, { type: 'block-start', index: 0, blockType: 'reasoning' }),
{ type: 'reasoning-chunks', time0: 210, index: 0, dt: [], texts: ['think'] },
@@ -266,8 +265,7 @@ describe('timing — the generation split (reasoning / text / tool args)', () =>
test('an unstamped call carries no split (its model time is unattributed wholesale)', () => {
// Block markers but no token anywhere in the stream: the call prices no
// generation time, so its spans must not reappear as generation the
// caller never charged — and its blocks count nothing either (the tally
// rides the same gate).
// caller never charged — and its blocks count nothing either (the tally rides the same gate).
const { state } = driveTimeline([
stepStart(1, { time: 0 }),
assistantMessage(2, {
@@ -410,8 +408,7 @@ describe('timing — served wire view', () => {
toolCall(5, { callId: 'c1', name: 'bash' }),
toolResult(6, { callId: 'c1', content: text('ok') }),
stepEnd(7, { time: 30_000 }),
// A step left OPEN with a stamped slot: the final state carries
// stepStart.{time,firstToken} for the gate.
// A step left OPEN with a stamped slot: the final state carries stepStart.{time,firstToken} for the gate.
stepStart(8, { time: 40_000 }),
assistantAttempt(9, { stream: [chunkRec(40_100, { type: 'text-delta', text: 'x' })] })])
// The 0.1.1+ contract validates persisted state through stateSchema.
@@ -519,8 +516,7 @@ describe('timing — the step\'s active time (user waits priced out)', () => {
})
test('a requestless step after a real one stamps nothing onto its record', () => {
// The seq guard: the open step armed no back-pointer, so the earlier
// step's record keeps its own stamp untouched.
// The seq guard: the open step armed no back-pointer, so the earlier step's record keeps its own stamp untouched.
const { state } = driveTimeline([
...step(1, 0, 1_000, { tokenMs: 200 }),
stepStart(4, { time: 10_000 }),
+1 -2
View File
@@ -16,8 +16,7 @@ const bucket = (uncached: number, cacheRead: number, cacheWrite: number, output:
describe('buildTimelineView unknown-model shape', () => {
test('optional scalars stay ABSENT keys until known — never undefined-valued (issue #29)', () => {
// A fold over events that name no model/provider/capacity: the served
// view must not carry `undefined`-valued properties, which fail the
// harness's lossless-JSON push pipeline whole.
// view must not carry `undefined`-valued properties, which fail the harness's lossless-JSON push pipeline whole.
const { view } = driveTimeline([
userMessage(1, [{ type: 'text', text: 'aaaa' }]),
assistantMessage(2, { turn: 1, step: 1 }),
+2 -4
View File
@@ -1,7 +1,6 @@
// Unit tests for the contextHeaders projection unit (src/host/headers.ts) —
// the request-header EPOCH METADATA behind the timeline's envelope figures.
// The unit is pure init/apply/view: each case drives real event envelopes
// through the real definition (no harness plumbing).
// The unit is pure init/apply/view: each case drives real event envelopes through the real definition (no harness plumbing).
import assert from 'node:assert/strict'
import { describe, test } from 'vitest'
@@ -66,8 +65,7 @@ describe('createContextHeadersDefinition', () => {
assert.equal(def.apply(state, headerEvent(seq, raw) as never), state, `header ${String(raw)} is not an epoch`)
}
// A data-less envelope is unreachable from the harness (append validates
// the payload) but must degrade to "not an epoch" all the same: this unit
// has no try/catch around its fold.
// the payload) but must degrade to "not an epoch" all the same: this unit has no try/catch around its fold.
assert.equal(def.apply(state, { type: 'request/header', seq: 5, time: 5000 } as never), state)
})
+7 -14
View File
@@ -1,7 +1,6 @@
// Fold drivers for the host specs: run event envelopes through the REAL
// projection units the plugin registers (no harness plumbing — the units are
// pure init/apply/view), pinning the state's plain-JSON rule on every
// intermediate state (see assertNoAbsentMembers).
// pure init/apply/view), pinning the state's plain-JSON rule on every intermediate state (see assertNoAbsentMembers).
import assert from 'node:assert/strict'
import type { SessionEvent } from '@deepseek-ai/dsh-session'
@@ -12,14 +11,12 @@ import type { ContextHeaders, ContextTimeline } from '../../../src/shared/types'
import { createContextTimelineDefinition } from '../../../src/host/timeline'
import { createContextHeadersDefinition } from '../../../src/host/headers'
/**
* The fold's declared contract takes the core `SessionEvent` union; the log
/** The fold's declared contract takes the core `SessionEvent` union; the log
* also carries declaration-merged plugin events (compaction/*), so the fold
* widens to TimelineEvent (src/host/fold.ts). These widened faces mirror the
* supported registry contract (src/host/compat.ts: stateSchema + required
* `wire`, zero-arg `init` — see ProjectionDefinition) and perform the ONE
* documented cast inside the helper, keeping every spec call site clean.
*/
* documented cast inside the helper, keeping every spec call site clean. */
export interface TimelineDefLike {
key: string
/** The persisted-state gate the projection cache's cold path parses rows with. */
@@ -48,14 +45,12 @@ export function headersDef(): HeadersDefLike {
return createContextHeadersDefinition() as unknown as HeadersDefLike
}
/**
* Lossless-JSON probe and detach, inlined with the dsh `snapshotJsonValue`
/** Lossless-JSON probe and detach, inlined with the dsh `snapshotJsonValue`
* semantics (the export lives in `@deepseek-ai/dsh-util-values`, and the
* test fixtures must track no single dsh face). Returns undefined when the
* value is not losslessly JSON-serializable: an undefined/function/symbol
* member, a non-finite number, a non-plain object, or a cycle. Shared with the
* compat matrix's registry driver (tests/host/compat/registryDriver.ts).
*/
* compat matrix's registry driver (tests/host/compat/registryDriver.ts). */
export function snapshotJson(value: unknown, ancestors: Set<object> = new Set()): unknown {
switch (typeof value) {
case 'string': case 'boolean': return value
@@ -91,11 +86,9 @@ export function snapshotJson(value: unknown, ancestors: Set<object> = new Set())
}
}
/**
* The projection-cache precondition: every state the fold produces must be
/** The projection-cache precondition: every state the fold produces must be
* losslessly JSON-serializable (a single undefined property fails EVERY cache
* write for the session — see TimelineState). Returns the detached copy.
*/
* write for the session — see TimelineState). Returns the detached copy. */
export function assertPlainJson<T>(state: T): T {
const copy = snapshotJson(state)
assert.ok(copy !== undefined, 'fold state must be losslessly JSON-serializable')
+1 -2
View File
@@ -1,8 +1,7 @@
// Integration tests for the Host-half plugin module (src/host/index.ts)
// against the REAL cordis registry, session store, and session-projection
// registry — the dsh-canonical harness: real envelopes appended to a real
// session, folded by the registered units, read back through the snapshot
// cut and the change feed.
// session, folded by the registered units, read back through the snapshot cut and the change feed.
import assert from 'node:assert/strict'
import { join } from 'node:path'
+3 -6
View File
@@ -2,8 +2,7 @@
// fold reconciles — the embedded assistant stream's first token, the
// replacement op's endpoints, and the raw chunk token test. Hostile shapes
// are pinned beside the happy paths: a malformed record must read as
// "nothing here", never throw (the projection registry drives the fold with
// no error boundary of its own).
// "nothing here", never throw (the projection registry drives the fold with no error boundary of its own).
import assert from 'node:assert/strict'
import { describe, test } from 'vitest'
@@ -43,8 +42,7 @@ describe('decodeTallyOfStream', () => {
})
test('a later block closes the previous one even when its own kind is unknown', () => {
// The unknown marker still ends the reasoning span; its own interval is
// lost and it counts nothing.
// The unknown marker still ends the reasoning span; its own interval is lost and it counts nothing.
assert.deepEqual(decodeTallyOfStream([chunk(1000, 'reasoning'), chunk(1400, 'image')], 1900), {
spans: { reasoning: 400, text: 0, toolarg: 0 },
blocks: { reasoning: 1, text: 0, toolarg: 0 },
@@ -83,8 +81,7 @@ describe('decodeTallyOfStream', () => {
})
test('a backwards marker clamps to a zero span instead of a negative one', () => {
// The text block's interval is negative → 0; the reasoning block is last,
// so it owns the tail to endTime.
// The text block's interval is negative → 0; the reasoning block is last, so it owns the tail to endTime.
assert.deepEqual(decodeTallyOfStream([chunk(1000, 'text'), chunk(900, 'reasoning')], 950), {
spans: { reasoning: 50, text: 0, toolarg: 0 },
blocks: { reasoning: 1, text: 1, toolarg: 0 },
+1 -2
View File
@@ -49,8 +49,7 @@ describe('estimateMessage', () => {
test('a retired wrapper block prices as generic JSON (the meter parity branch)', () => {
// The supported log generation carries no wrapper blocks; a hostile one
// prices through the same structural-JSON arm the meter applies to every
// unknown block.
// prices through the same structural-JSON arm the meter applies to every unknown block.
const block = { type: 'tool-result', content: [{ type: 'text', text: 'ok' }] }
assert.equal(
estimateMessage({ content: [block] }),
+3 -7
View File
@@ -3,8 +3,7 @@
// harness's own skill catalog (observeSession → cwd + preset; a live agent's
// scoped registry wins; a cold session rides a scope lease; every gap falls
// back to the global layers), the typed `null` for every unusable face (the
// route must never throw into the transport), the per-entry narrowing, and
// the serve bound.
// route must never throw into the transport), the per-entry narrowing, and the serve bound.
import assert from 'node:assert/strict'
import { describe, test } from 'vitest'
@@ -21,11 +20,8 @@ interface CtxSpec {
agentPresets?: unknown
}
/**
* A minimal host ctx double with cordis inject semantics (mirrors
* balance.spec.ts's): the callback runs once its dependency list completes,
* and `ctx.get` serves the request-time reads.
*/
/** A minimal host ctx double with cordis inject semantics (mirrors
* balance.spec.ts's): the callback runs once its dependency list completes, and `ctx.get` serves the request-time reads. */
function ctxOf(spec: CtxSpec): { ctx: Context; captured: { path?: string; fetch?: RouteFetch } } {
const captured: { path?: string; fetch?: RouteFetch } = {}
const services = new Map<string, unknown>()
+2 -4
View File
@@ -1,7 +1,6 @@
// Unit tests for host/toolSources.ts — best-effort tool-to-plugin attribution:
// MCP server recovery from the `mcp__` name prefix, and the pinned first-party
// package map. (A harness-logged `plugin` field, when one appears, passes
// through headers.ts ahead of everything here.)
// package map. (A harness-logged `plugin` field, when one appears, passes through headers.ts ahead of everything here.)
import assert from 'node:assert/strict'
import { describe, test } from 'vitest'
@@ -29,8 +28,7 @@ describe('mcpServerOf', () => {
test('hash-appended overlong names resolve the server that survived truncation', () => {
const hex64 = '0123456789abcdef'.repeat(4)
// Overlong names get '_<64-hex>' appended; the last __ separator survives
// unless the truncation cut into it.
// Overlong names get '_<64-hex>' appended; the last __ separator survives unless the truncation cut into it.
assert.equal(mcpServerOf(`mcp__github__tool_${hex64}`), 'github')
assert.equal(mcpServerOf(`mcp__github__tool_${'z'.repeat(64)}`), 'github')
})
+18 -42
View File
@@ -1,19 +1,14 @@
// The runtime harness-version probe (src/host/version.ts): the running-module
// anchor and its own-closure guard, the home-mirror anchor, both probe paths
// (manifest subpath / entry ascend), and every degradation arm — the gate's
// fail-open promise rests on ALL of these returning undefined instead of
// throwing.
//
// Fixture roots:
// - tests/host/fixtures/version/homes/* — committed homes whose packages
// fail-open promise rests on ALL of these returning undefined instead of throwing.
// Fixture roots: - tests/host/fixtures/version/homes/* — committed homes whose packages
// RESOLVE at the fixture level (no walk-up), shared with index.spec.ts.
// - a per-run tree under os.tmpdir() — the FAILURE cases and the
// running-tree fixtures. They must sit outside the repository: a failing
// probe inside the repo tree would walk up into the repo's own node_modules
// and answer with the pinned devDependencies instead of failing, and a
// running-tree witness inside the repo would read as this package's own
// dependency closure.
//
// running-tree witness inside the repo would read as this package's own dependency closure.
// Walk-up cleanliness alone is not hermetic, though: CJS resolution consults
// NODE_PATH after the walk-up fails, and a pnpm-managed environment (a
// `pnpm` binary from a sibling checkout) injects that checkout's virtual
@@ -24,11 +19,9 @@
// package but carries no version, so both probe paths (manifest subpath via
// the exports gate, entry ascend via the versionless own manifest) yield
// undefined and the degradation arms stay machine-independent.
//
// The running anchor is injected as a URL resolver, and the plugin root as a
// path, so both the trusted and the own-closure arms are exercised hermetically
// (the repo's real defaults are covered by the last case of the degradation
// block).
// (the repo's real defaults are covered by the last case of the degradation block).
import assert from 'node:assert/strict'
import { mkdirSync, mkdtempSync, rmSync, writeFileSync } from 'node:fs'
@@ -53,11 +46,8 @@ function ctxWithHome(homePath: unknown): Context {
return ctx
}
/**
* The running anchor's URL resolver for one fixture home under `root`: real
* Node resolution from that home's profiles dir, as an installed plugin's own
* module pipeline would perform it.
*/
/** The running anchor's URL resolver for one fixture home under `root`: real
* Node resolution from that home's profiles dir, as an installed plugin's own module pipeline would perform it. */
function runningResolver(home: string, root: string = HOMES): (specifier: string) => string {
const anchor = join(root, home, 'profiles', 'running-probe.cjs')
return specifier => pathToFileURL(createRequire(anchor).resolve(specifier)).href
@@ -66,10 +56,8 @@ function runningResolver(home: string, root: string = HOMES): (specifier: string
/** A running anchor that never answers, forcing the home anchor. */
const NO_RUNNING = (): string => { throw new Error('no running module tree') }
/**
* A plugin root that contains no fixture tree — the trusted-witness case.
* It sits in os.tmpdir() beside (never above) the scratch root.
*/
/** A plugin root that contains no fixture tree — the trusted-witness case.
* It sits in os.tmpdir() beside (never above) the scratch root. */
const ELSEWHERE = join(tmpdir(), 'dsh-context-plugin-root')
/** A running anchor that always answers with one fixed URL. */
@@ -77,12 +65,9 @@ const fixedUrl = (url: string) => (): string => url
let scratch = ''
/**
* Write one scratch probe home. `manifest` is the literal package.json
/** Write one scratch probe home. `manifest` is the literal package.json
* content of the given package; `entry` (when set) becomes the file the
* manifest's `exports['.']` points at, with `decoy` an optional mismatched
* manifest placed beside it.
*/
* manifest's `exports['.']` points at, with `decoy` an optional mismatched manifest placed beside it. */
function writeScratchHome(
home: string,
packageName: string,
@@ -105,13 +90,10 @@ function writeScratchHome(
const scratchResolver = (home: string): (...segments: string[]) => string => resolverIn(join(scratch, home))
/**
* A probe-package shadow: the package RESOLVES at the fixture level (so the
/** A probe-package shadow: the package RESOLVES at the fixture level (so the
* walk-up — and the NODE_PATH ambient installs behind it — are never
* consulted) but answers NO version. The manifest subpath probe dies on the
* exports gate (no './package.json' export), and the entry ascend reads the
* package's own versionless manifest to undefined.
*/
* exports gate (no './package.json' export), and the entry ascend reads the package's own versionless manifest to undefined. */
function shadow(home: string, packageName: string): void {
writeScratchHome(home, packageName,
JSON.stringify({ name: packageName, exports: { '.': './lib/index.js' } }),
@@ -123,8 +105,7 @@ beforeAll(() => {
// The CLI-row-absent fall-through lives in scratch: inside the repo tree a
// missing CLI package could resolve to an ambient install above the repo
// (e.g. a global copy under ~/node_modules), making the answer machine-
// dependent; outside it the walk-up is provably clean. The shadows close
// the remaining NODE_PATH route (see the header note).
// dependent; outside it the walk-up is provably clean. The shadows close the remaining NODE_PATH route (see the header note).
shadow('library-only', '@deepseek-ai/dsh')
writeScratchHome('library-only', '@deepseek-ai/dsh-session-projection',
JSON.stringify({ name: '@deepseek-ai/dsh-session-projection', version: '0.1.1-rc.2' }))
@@ -135,8 +116,7 @@ beforeAll(() => {
JSON.stringify({ name: '@deepseek-ai/dsh', version: '0.1.1-rc.4', exports: { '.': './a/b/index.js' } }),
{ path: 'a/b/index.js', decoyManifest: JSON.stringify({ name: '@deepseek-ai/dsh-decoy', version: '9.9.9' }) })
// Odd manifests sit as DECOYS on the ascend path: only the direct
// manifest read (not a resolve) ever surfaces them, and the probe must
// skip each to reach the owning package's real version.
// manifest read (not a resolve) ever surfaces them, and the probe must skip each to reach the owning package's real version.
writeScratchHome('entry-badjson', '@deepseek-ai/dsh',
JSON.stringify({ name: '@deepseek-ai/dsh', version: '0.1.1-rc.5', exports: { '.': './a/index.js' } }),
{ path: 'a/index.js', decoyManifest: 'not json{' })
@@ -180,8 +160,7 @@ beforeAll(() => {
writeScratchHome('running-supported', '@deepseek-ai/dsh-session',
JSON.stringify({ name: '@deepseek-ai/dsh-session', version: '0.1.7-rc.2', exports: { '.': './lib/index.js' } }),
{ path: 'lib/index.js' })
// The just-dropped line: below the baseline since the floor moved to
// 0.1.7-rc.2, so the gate must trip on it.
// The just-dropped line: below the baseline since the floor moved to 0.1.7-rc.2, so the gate must trip on it.
writeScratchHome('running-old', '@deepseek-ai/dsh-session',
JSON.stringify({ name: '@deepseek-ai/dsh-session', version: '0.1.5-rc.1', exports: { '.': './lib/index.js' } }),
{ path: 'lib/index.js' })
@@ -205,23 +184,20 @@ describe('detectHarnessVersion — running anchor', () => {
})
test('a below-baseline running tree still trips the gate over a newer mirror', () => {
// The reverse direction must hold too: a genuinely old harness is reported
// even when the mirror names something newer.
// The reverse direction must hold too: a genuinely old harness is reported even when the mirror names something newer.
assert.equal(detectHarnessVersion(ctxWithHome(homeResolver('future')), runningResolver('running-old', scratch), ELSEWHERE), '0.1.5-rc.1')
})
test('discards a witness inside this package own closure for the home anchor', () => {
// A `link:`-installed dev checkout resolves its own pinned devDependencies:
// that closure is not the harness, so the home mirror answers instead. The
// default package root (the real one, which contains the repo's
// node_modules) performs the discard.
// default package root (the real one, which contains the repo's node_modules) performs the discard.
const ctx = ctxWithHome(homeResolver('old'))
assert.equal(detectHarnessVersion(ctx, runningResolver('module-skips-cli')), '0.1.5-rc.1')
})
test('never probes the CLI package from the running anchor', () => {
// The fixture pins the CLI at 0.0.1 but the libraries at the baseline:
// only a library answer may come back.
// The fixture pins the CLI at 0.0.1 but the libraries at the baseline: only a library answer may come back.
const ctx = ctxWithHome(scratchResolver('empty'))
assert.equal(detectHarnessVersion(ctx, runningResolver('module-skips-cli'), ELSEWHERE), '0.1.7-rc.2')
})
+1 -2
View File
@@ -79,8 +79,7 @@ describe('DAY_KEY_RE', () => {
describe('startOfDayKey / endOfDayKey', () => {
test('a picked range spans the whole local day', () => {
// Endpoints, not a duration: a DST-short day is 23 hours long, so the
// difference is not a fixed number of milliseconds.
// Endpoints, not a duration: a DST-short day is 23 hours long, so the difference is not a fixed number of milliseconds.
assert.equal(startOfDayKey('2026-09-16'), new Date(2026, 8, 16, 0, 0, 0, 0).getTime())
assert.equal(endOfDayKey('2026-09-16'), new Date(2026, 8, 16, 23, 59, 59, 999).getTime())
assert.equal(startOfDayKey('2026-03-08'), new Date(2026, 2, 8, 0, 0, 0, 0).getTime(), 'a DST-short day still opens on its own midnight')
+1 -2
View File
@@ -1,8 +1,7 @@
// The shared token heuristics (src/shared/estimate.ts): the fixed-density
// figures the host fold and the client boundary must agree on. The system
// prompt is priced from a `system/message`'s content blocks (or a rendered
// string, at the legacy-wire boundary) — with the harness token-meter's own
// no-per-block-overhead rule.
// string, at the legacy-wire boundary) — with the harness token-meter's own no-per-block-overhead rule.
import assert from 'node:assert/strict'
import { describe, test } from 'vitest'
+2 -4
View File
@@ -2,8 +2,7 @@
// assembly consumed by the host fold and the client's inline-generation
// fallback alike. Pins the whole producer matrix: the six built-ins, the
// Anthropic-style editor, the search meta attribution (complete / truncated
// / malformed / args-less), the read window vs the limit estimate, the
// pattern-target fallback, and the zero-ops degradations.
// / malformed / args-less), the read window vs the limit estimate, the pattern-target fallback, and the zero-ops degradations.
import assert from 'node:assert/strict'
import { describe, test } from 'vitest'
@@ -89,8 +88,7 @@ describe('opsOfCall — one op per settled call', () => {
argsRaw: JSON.stringify({ pattern: 'TODO' }),
meta: grepMeta({ 'a.ts': 3, 'b.ts': 1 }),
})
// The searched path/pattern rows first (here the workspace-wide pattern),
// then each hit file with its count.
// The searched path/pattern rows first (here the workspace-wide pattern), then each hit file with its count.
assert.deepEqual(ops.map(o => [o.path, o.hits]), [['TODO', undefined], ['a.ts', 3], ['b.ts', 1]])
assert.equal(ops[0].pattern, true, 'the pathless target rows as the pattern')
assert.ok(ops.slice(1).every(o => o.detail === 'TODO'), 'the pattern detail rides each hit op')
+1 -2
View File
@@ -1,6 +1,5 @@
// The version gate's arithmetic (src/shared/version.ts): parsing, the total
// order (release > rc > beta > alpha at equal X.Y.Z), and the fail-open
// baseline check.
// order (release > rc > beta > alpha at equal X.Y.Z), and the fail-open baseline check.
import assert from 'node:assert/strict'
import { describe, test } from 'vitest'