diff --git a/docs/compatibility.md b/docs/compatibility.md index 9b071c3..8f0922d 100644 --- a/docs/compatibility.md +++ b/docs/compatibility.md @@ -145,7 +145,7 @@ The trend chart's duration overlay (the card's Duration toggle: each request's s The session node's answers tally (`answers` on the `contextTimeline` head — one per assistant message carrying a non-blank text block; steps that only dispatched tool calls are work, not replies) bumps the timeline projection's `stateVersion` (26 → 27): it is a running total over the complete log that later events cannot backfill for sessions folded before the field existed (the v16 `humanInputs` precedent), so on upgrade every cached timeline row is invalidated and refolded from the durable log, while the v20 warm-up rebuilds idle sessions' rows the first time the page opens. On the wire the field is additive-optional: a row folded before it existed serves without it and the card degrades to a zero figure. The head's sibling `counts.skills` figure (the distinct loaded-skill names) needed no bump of its own — it is derived at view time over the retained events already living in every cached row's state (skill-tagged inject events since v19), so a pre-field row recomputes the exact tally the moment this build serves it. -The Current Context bar's two auto-compaction reserve masks need the routed request's output cap, which bumps the timeline projection's `stateVersion` (27 → 28): the cap is the newest `request/header` envelope's materialized `config.maxTokens`, folded last-wins exactly as the compaction backend's own `reservedCompletionTokens` reads it, and a header event already folded can never be replayed into a cached row while its session is idle; on upgrade every cached timeline row is invalidated and refolded from the durable log, while the v20 warm-up rebuilds idle sessions' rows the first time the page opens. On the wire the field is additive-optional: a row folded before it existed, or a log whose header carries no cap, serves without it and the bar paints the headroom mask alone (the client re-derives the reserve per serve — `src/client/reserve.ts`), the same sanitizer contract every other scalar rides. +The Current Context bar's two auto-compaction reserve masks need the routed request's output cap, which bumps the timeline projection's `stateVersion` (27 → 28): the cap is the newest `request/header` envelope's materialized `config.maxTokens`, folded last-wins exactly as the compaction backend's own `reservedCompletionTokens` reads it, and a header event already folded can never be replayed into a cached row while its session is idle; on upgrade every cached timeline row is invalidated and refolded from the durable log, while the v20 warm-up rebuilds idle sessions' rows the first time the page opens. On the wire the field is additive-optional: a row folded before it existed, or a log whose header carries no cap, serves without it and the bar paints the headroom mask without the output one (the client re-derives the reserve per serve — `src/client/reserve.ts`), the same sanitizer contract every other scalar rides. The Fleet tab's per-agent detail (delegation prompt, descriptor composition, latest reply, inter-agent messages, and the lead's roster facts) rides the plugin's `/api/dsh-context/fleet` fetch route — the same authenticated `/api` fence, Connection exact-route registry, and cold-read gate as the detail route, folding nothing token-shaped of its own: it lifts `subagent/descriptor`, `team/member`, and `user/message` relay/mailbox/settled records out of one `sessionQuery.observeSession` read per session (live-preferred, so live and cold sessions share the path). Every seam degrades independently: a deployment without the route (or a skipped cold read) renders the inspector's core and hides the detail sections; a harness without the experimental Agent Teams plugin serves no `agentTeam` projection, so the members board and task board simply never render; the relay/mailbox/settled source kinds absent from a session's log yield an empty comms timeline, not an error. Sender-framing preambles ("Agent sent a message:", "Team message from :", "Team message from :") strip at extraction; the `subagent-settled` English boilerplate maps to localized variants at render, with unrecognized phrasings falling back to the raw text. diff --git a/src/client/components/stackedBar.tsx b/src/client/components/stackedBar.tsx index 6105fad..6a85b52 100644 --- a/src/client/components/stackedBar.tsx +++ b/src/client/components/stackedBar.tsx @@ -46,17 +46,17 @@ export interface StackedBarProps { /** Render the hover tooltip (default true); a bar that only MIRRORS another card's hover turns it off, so the tooltip * floats only over the surface the pointer actually rests on. */ tip?: boolean - /** Optional auto-compaction reserve: the two masks right of the pressure trigger — the compaction - * headroom, then the routed output reservation at the window's end (reserve.ts derives them). Laid - * out in WINDOW tokens and rendered only when `max` (the window) is positive. */ + /** Optional auto-compaction reserve (reserve.ts derives the geometry): the two masks tiling the + * window's tail — headroom, then the output reservation — plus the trigger's line when it detaches. + * Laid out in WINDOW tokens and rendered only when `max` (the window) is positive. */ reserve?: ContextReserve } export function makeStackedBar(kit: ViewKit): (props: StackedBarProps) => ReactElement { const { t, fmt, catLabel } = kit return function StackedBar(props: StackedBarProps): ReactElement { - // The reserve-hover key lives here so the single tooltip slot serves both the segments and the masks. - const [reserveOn, setReserveOn] = useState(null) + // The reserve-hover key lives here so the single tooltip slot serves segments, masks, and the trigger line. + const [reserveOn, setReserveOn] = useState(null) const wrapRef = useRef(null) const tipRef = useRef(null) const [tipShift, setTipShift] = useState(0) @@ -94,37 +94,55 @@ export function makeStackedBar(kit: ViewKit): (props: StackedBarProps) => ReactE // The masks lay out in WINDOW units scaled onto whatever track the bar spans, so they keep the same // physical slice with or without a free track (once used exceeds the window, the stripes sit over the - // outermost segments). The two tile `[threshold, window]` end to end, and a mask nothing wide enough - // to hover is dropped rather than rendered sub-pixel. - const reserve = props.reserve !== undefined && props.max !== undefined && props.max > 0 ? props.reserve : undefined + // outermost segments). They tile the window's tail at their true sizes — [window − output − headroom, + // window] — and a mask nothing wide enough to hover is dropped rather than rendered sub-pixel. + const win = props.max !== undefined && props.max > 0 ? props.max : undefined + const reserve = win === undefined ? undefined : props.reserve const trackPct = (tokens: number): number => Math.round(Math.max(0, Math.min(100, tokens / scale * 100)) * 10) / 10 - // The headroom mask's left edge IS the trigger position, so read it off the same projection the masks use - // rather than a second ratio — that one is clamped to the window, so no impossible percent reaches the copy. + // Read the trigger off the same projection the masks use — that one is clamped to the window, + // so no impossible percent reaches the copy. const triggerPct = reserve === undefined ? 0 : trackPct(reserve.threshold) const masks: ReserveMask[] = [] - if (reserve !== undefined) { + let triggerDetached = false + if (reserve !== undefined && win !== undefined) { + const maskStart = win - reserve.output - reserve.headroom const pieces = [ - { key: 'headroom' as const, label: t('overview.reserveHeadroom'), tokens: reserve.headroom, from: reserve.threshold }, - { key: 'output' as const, label: t('overview.reserveOutput'), tokens: reserve.output, from: reserve.threshold + reserve.headroom }, + { key: 'headroom' as const, label: t('overview.reserveHeadroom'), tokens: reserve.headroom, from: maskStart }, + { key: 'output' as const, label: t('overview.reserveOutput'), tokens: reserve.output, from: win - reserve.output }, ] for (const piece of pieces) { const left = trackPct(piece.from) const width = trackPct(piece.from + piece.tokens) - left if (width > 0) masks.push({ key: piece.key, label: piece.label, tokens: piece.tokens, left, width }) } + triggerDetached = reserve.threshold < maskStart } + // Every reserve piece — masks and the trigger line — shares the one hover key and pointer handling. + const enterReserve = (key: ReserveMask['key'] | 'trigger') => (): void => { + setReserveOn(key) + if (props.onHoverKey !== undefined) props.onHoverKey(null) + } + const leaveReserve = (): void => { setReserveOn(null) } + // The tooltip is DERIVED from the shared hover key, so a segment and its legend chip light the same segment with the same tooltip; - // the wrapper keeps it outside the clipped stack; a hovered reserve mask overrides the slot with its own rows. Every figure NAMES its - // denominator — the segments share the USED total, the free track the WINDOW, the masks state only where they sit in the window. + // the wrapper keeps it outside the clipped stack; a hovered reserve piece (mask or trigger line) overrides the slot with its own + // rows. Every figure NAMES its denominator — the segments share the USED total, the free track and the trigger line the WINDOW. let tip: BarTip | null = null const hoveredMask = reserveOn === null ? undefined : masks.find(m => m.key === reserveOn) - if (hoveredMask !== undefined) { + if (reserveOn === 'trigger' && reserve !== undefined && triggerDetached) { + tip = { + head: t('overview.reserveTrigger'), + figure: `${fmt(reserve.threshold)} · ${triggerPct}% ${t('overview.ofWindow')}`, + note: t('overview.reserveTriggerTip'), + leftPct: triggerPct, + } + } else if (hoveredMask !== undefined) { tip = { head: hoveredMask.label, figure: fmt(hoveredMask.tokens), note: hoveredMask.key === 'headroom' - ? t('overview.reserveHeadroomTip', { p: triggerPct }) + ? (triggerDetached ? t('overview.reserveHeadroomBuffer') : t('overview.reserveHeadroomTip', { p: triggerPct })) : t('overview.reserveOutputTip'), leftPct: hoveredMask.left + hoveredMask.width / 2, } @@ -225,13 +243,18 @@ export function makeStackedBar(kit: ViewKit): (props: StackedBarProps) => ReactE key={m.key} className={`lc-reserve lc-reserve-${m.key}`} style={{ left: `${m.left}%`, width: `${m.width}%` }} - onMouseEnter={() => { - setReserveOn(m.key) - if (props.onHoverKey !== undefined) props.onHoverKey(null) - }} - onMouseLeave={() => { setReserveOn(null) }} + onMouseEnter={enterReserve(m.key)} + onMouseLeave={leaveReserve} /> ))} + {triggerDetached ? ( +
+ ) : null} {/* The occupied-region frame: painted last so its border stays above the segments; always mounted (`.lc-occupied-box-on` toggles opacity) so it fades out on leave instead of unmounting instantly. */} diff --git a/src/client/i18nEn.ts b/src/client/i18nEn.ts index 9aed150..54966e5 100644 --- a/src/client/i18nEn.ts +++ b/src/client/i18nEn.ts @@ -22,7 +22,10 @@ export const DICT_EN = { 'overview.reserveHeadroom': 'Compaction headroom', 'overview.reserveOutput': 'Max output tokens', 'overview.reserveHeadroomTip': 'Auto-compaction fires at {p}% of the window', + 'overview.reserveHeadroomBuffer': 'Buffer kept free ahead of the output reservation', 'overview.reserveOutputTip': 'This reply\'s output is reserved at the window\'s end', + 'overview.reserveTrigger': 'Auto-compaction trigger', + 'overview.reserveTriggerTip': 'Compaction fires when the used tokens reach this line', 'stats.title': 'Context Stats', 'stats.session': 'Current Session', 'stats.turns': 'Turns', diff --git a/src/client/i18nZh.ts b/src/client/i18nZh.ts index 017f227..152104f 100644 --- a/src/client/i18nZh.ts +++ b/src/client/i18nZh.ts @@ -19,7 +19,10 @@ export const DICT_ZH = { 'overview.reserveHeadroom': '压缩余量', 'overview.reserveOutput': '最大输出预留', 'overview.reserveHeadroomTip': '自动压缩在窗口的 {p}% 处触发', + 'overview.reserveHeadroomBuffer': '在输出预留之前留出的缓冲', 'overview.reserveOutputTip': '本次回复的输出预留在窗口末尾', + 'overview.reserveTrigger': '自动压缩触发线', + 'overview.reserveTriggerTip': '已用量到达此线时触发压缩', 'stats.title': '上下文统计', 'stats.session': '当前会话', 'stats.turns': '轮次', diff --git a/src/client/reserve.ts b/src/client/reserve.ts index 415b41d..b4bbb01 100644 --- a/src/client/reserve.ts +++ b/src/client/reserve.ts @@ -1,7 +1,7 @@ /** - * The auto-compaction reserve the Current Context bar masks: the window the session is kept out of - * once compaction would fire, split into its two parts — the compaction headroom and the routed - * request's output reservation. + * The auto-compaction reserve the Current Context bar masks: the window's tail the session is kept + * out of, at its two true sizes — the compaction headroom buffer, then the routed request's output + * reservation at the window's end. * * dsh-compaction-basic prices its pressure trigger as * `thresholdTokens = floor(min(contextWindow × thresholdRatio, contextWindow − reservedOutput − headroomTokens))` @@ -21,9 +21,11 @@ export const COMPACT_HEADROOM_TOKENS = 65_536 /** Where the reserve starts and what it is made of, in WINDOW tokens. */ export interface ContextReserve { - /** Window tokens at which automatic pressure compaction triggers — the masks' left edge. */ + /** Window tokens at which automatic pressure compaction triggers: the headroom mask's left edge + * when the pressure budget binds, strictly left of the masks when the window fraction does. */ threshold: number - /** Window tokens between the trigger and the output reservation — the compaction-headroom mask. */ + /** The compaction-headroom mask — always the mirrored policy value, the buffer between the + * pressure budget and the output reservation. */ headroom: number /** The routed request's output reservation at the window's end — the max-output mask. */ output: number @@ -31,9 +33,9 @@ export interface ContextReserve { /** Resolve one window's reserve, or undefined when no positive window is known OR the harness * itself refuses to configure pressure compaction for that route (see the budget check below). The - * masks tile `[threshold, window]` end to end; `maxOutputTokens` is untrusted wire input, and - * anything but a positive finite cap reserves nothing (dsh's own fallback order: envelope cap, - * else zero). */ + * masks tile the window's tail `[window − output − headroom, window]` end to end, each at its true + * size; `maxOutputTokens` is untrusted wire input, and anything but a positive finite cap reserves + * nothing (dsh's own fallback order: envelope cap, else zero). */ export function reserveGeometry( window: number | undefined, maxOutputTokens: number | undefined, @@ -50,5 +52,5 @@ export function reserveGeometry( const pressureBudget = window - output - COMPACT_HEADROOM_TOKENS if (pressureBudget <= 0) return undefined const threshold = Math.min(Math.floor(window * COMPACT_THRESHOLD_RATIO), pressureBudget) - return { threshold, headroom: window - output - threshold, output } + return { threshold, headroom: COMPACT_HEADROOM_TOKENS, output } } diff --git a/src/client/styles/stackedBar.css b/src/client/styles/stackedBar.css index 0ea25da..f0d04c3 100644 --- a/src/client/styles/stackedBar.css +++ b/src/client/styles/stackedBar.css @@ -25,6 +25,10 @@ .lc-reserve { position: absolute; top: 0; bottom: 0; z-index: 1; pointer-events: auto; cursor: help; } .lc-reserve-headroom { background: repeating-linear-gradient(45deg, color-mix(in srgb, var(--dsw-alias-state-warn-primary) 24%, transparent) 0 5px, transparent 5px 10px); } .lc-reserve-output { background: repeating-linear-gradient(-45deg, color-mix(in srgb, var(--dsw-alias-state-business-primary) 24%, transparent) 0 5px, transparent 5px 10px); } +/* The trigger line (painted only when it detaches from the masks — reserve.ts): a 2px rule inside a + wider lane so the hover target stays hittable; unanimated like the masks, same warn hue. */ +.lc-trigger { position: absolute; top: 0; bottom: 0; width: 9px; transform: translateX(-50%); z-index: 1; pointer-events: auto; cursor: help; } +.lc-trigger::after { content: ''; position: absolute; top: 0; bottom: 0; left: 50%; width: 2px; margin-left: -1px; background: var(--dsw-alias-state-warn-primary); } /* Dashed border, not box-shadow: a shadow cannot dash. */ .lc-stacked-free-on { border: 2px dashed var(--dsw-alias-label-secondary); border-radius: 3px; } .lc-stacked-dim .lc-stacked-seg { opacity: 0.35; } diff --git a/tests/client/components/stackedBar.spec.ts b/tests/client/components/stackedBar.spec.ts index e8872fd..088646c 100644 --- a/tests/client/components/stackedBar.spec.ts +++ b/tests/client/components/stackedBar.spec.ts @@ -233,7 +233,7 @@ describe('StackedBar hover link', () => { describe('StackedBar reserve masks', () => { const RESERVE = { threshold: 800, headroom: 100, output: 100 } - test('the two masks tile the window tail; each hover shows its own figure, trigger note and anchor', async () => { + test('a bound pressure budget: the two masks tile the window tail from the trigger, no line', async () => { const m = await mount(h(HoverHarness, { parts: [part('user', 100)], max: 1000, reserve: RESERVE })) const headroom = query(m.container, '.lc-reserve-headroom') assert.equal(headroom.style.left, '80%') @@ -241,6 +241,8 @@ describe('StackedBar reserve masks', () => { const output = query(m.container, '.lc-reserve-output') assert.equal(output.style.left, '90%') assert.equal(output.style.width, '10%') + // The headroom mask's own left edge IS the trigger, so no separate line is drawn. + assert.equal(queryAll(m.container, '.lc-trigger').length, 0) await hover(headroom) const tip = query(m.container, '.lc-bar-tip') assert.ok(tip.className.includes('lc-bar-tip-on')) @@ -258,6 +260,32 @@ describe('StackedBar reserve masks', () => { await m.unmount() }) + test('a bound window fraction: true-size masks at the window tail, the trigger as its own line', async () => { + // threshold 800 sits strictly left of the masks' 840 edge: headroom + output (160) < 20% of the window. + const reserve = { threshold: 800, headroom: 60, output: 100 } + const m = await mount(h(HoverHarness, { parts: [part('user', 100)], max: 1000, reserve })) + const headroom = query(m.container, '.lc-reserve-headroom') + assert.equal(headroom.style.left, '84%') + assert.equal(headroom.style.width, '6%') + const output = query(m.container, '.lc-reserve-output') + assert.equal(output.style.left, '90%') + assert.equal(output.style.width, '10%') + const line = query(m.container, '.lc-trigger') + assert.equal(line.style.left, '80%') + // The headroom mask describes only itself; the trigger fact belongs to the line. + await hover(headroom) + assert.deepEqual(tipRows(query(m.container, '.lc-bar-tip')), ['Compaction headroom', '60', 'Buffer kept free ahead of the output reservation']) + await unhover(headroom) + await hover(line) + const tip = query(m.container, '.lc-bar-tip') + assert.deepEqual(tipRows(tip), ['Auto-compaction trigger', '800 · 80% of window', 'Compaction fires when the used tokens reach this line']) + assert.equal(tip.style.left, '80%') + assert.equal(query(m.container, '.lc-stacked').className, 'lc-stacked') // hoverKey cleared + await unhover(line) + assert.equal(query(m.container, '.lc-bar-tip').className, 'lc-tip lc-bar-tip') + await m.unmount() + }) + test('a zero-extent mask is dropped; an output-only reserve renders the rightmost mask alone', async () => { const m = await mount(h(HoverHarness, { parts: [part('user', 100)], @@ -265,12 +293,14 @@ describe('StackedBar reserve masks', () => { reserve: { threshold: 1000, headroom: 0, output: 0 }, })) assert.equal(queryAll(m.container, '.lc-reserve').length, 0) + assert.equal(queryAll(m.container, '.lc-trigger').length, 0) await m.update(h(HoverHarness, { parts: [part('user', 100)], max: 1000, reserve: { threshold: 900, headroom: 0, output: 100 }, })) assert.equal(queryAll(m.container, '.lc-reserve-headroom').length, 0) + assert.equal(queryAll(m.container, '.lc-trigger').length, 0) const output = query(m.container, '.lc-reserve-output') assert.equal(output.style.left, '90%') assert.equal(output.style.width, '10%') @@ -294,8 +324,10 @@ describe('StackedBar reserve masks', () => { const reserve = RESERVE const m = await mount(h(StackedBar, { parts: [part('user', 100)], reserve })) assert.equal(queryAll(m.container, '.lc-reserve').length, 0) + assert.equal(queryAll(m.container, '.lc-trigger').length, 0) await m.update(h(StackedBar, { parts: [part('user', 100)], max: 0, reserve })) assert.equal(queryAll(m.container, '.lc-reserve').length, 0) + assert.equal(queryAll(m.container, '.lc-trigger').length, 0) await m.unmount() }) }) diff --git a/tests/client/headline.spec.ts b/tests/client/headline.spec.ts index b7885fd..0307c35 100644 --- a/tests/client/headline.spec.ts +++ b/tests/client/headline.spec.ts @@ -138,16 +138,16 @@ describe('headlineOf reserve', () => { assert.deepEqual(h.reserve, { threshold: 678_464, headroom: 65_536, output: 256_000 }) }) - test('without an output cap only the headroom mask is left (the whole tail up to the ratio)', () => { + test('without an output cap only the headroom mask is left (at its true size, the trigger detached)', () => { const h = headlineOf(timeline({ contextWindow: 1_000_000 }), null) - assert.deepEqual(h.reserve, { threshold: 800_000, headroom: 200_000, output: 0 }) + assert.deepEqual(h.reserve, { threshold: 800_000, headroom: 65_536, output: 0 }) }) test('a hostile or non-positive cap reserves nothing; no window yields no reserve at all', () => { const hostile = headlineOf(timeline({ contextWindow: 1_000_000, maxOutputTokens: 'junk' as unknown as number }), null) - assert.deepEqual(hostile.reserve, { threshold: 800_000, headroom: 200_000, output: 0 }) + assert.deepEqual(hostile.reserve, { threshold: 800_000, headroom: 65_536, output: 0 }) const zero = headlineOf(timeline({ contextWindow: 1_000_000, maxOutputTokens: 0 }), null) - assert.deepEqual(zero.reserve, { threshold: 800_000, headroom: 200_000, output: 0 }) + assert.deepEqual(zero.reserve, { threshold: 800_000, headroom: 65_536, output: 0 }) assert.equal(headlineOf(timeline({ maxOutputTokens: 256_000 }), null).reserve, undefined) }) diff --git a/tests/client/reserve.spec.ts b/tests/client/reserve.spec.ts index 582a327..6ac322a 100644 --- a/tests/client/reserve.spec.ts +++ b/tests/client/reserve.spec.ts @@ -1,14 +1,16 @@ // The auto-compaction reserve geometry (src/client/reserve.ts): dsh-compaction-basic's pressure // trigger priced against the session's real window and routed output cap, plus the mask extents the -// composition bar paints. The two masks must always tile [threshold, window]. +// composition bar paints. The masks tile the window's tail [window − output − headroom, window] at +// their true sizes; the trigger sits at or left of the headroom mask's left edge. import assert from 'node:assert/strict' import { describe, test } from 'vitest' import { COMPACT_HEADROOM_TOKENS, COMPACT_THRESHOLD_RATIO, reserveGeometry } from '../../src/client/reserve' -/** The reserve always closes the window: trigger + headroom mask + output mask. */ -function tiles(reserve: NonNullable>, window: number): boolean { - return reserve.threshold + reserve.headroom + reserve.output === window +/** The trigger never crosses the masks: it IS the headroom mask's left edge when the pressure + * budget binds, and sits strictly left of it when the window fraction binds. */ +function triggerAtOrLeftOfMasks(reserve: NonNullable>, window: number): boolean { + return reserve.threshold <= window - reserve.output - reserve.headroom } describe('reserveGeometry', () => { @@ -20,25 +22,25 @@ describe('reserveGeometry', () => { assert.equal(reserveGeometry(Number.POSITIVE_INFINITY, 256_000), undefined) }) - test('the deepseek defaults: the pressure budget binds, and the headroom mask IS headroomTokens', () => { + test('the deepseek defaults: the pressure budget binds, and the trigger is the masks\' left edge', () => { // W = 1,000,000, O = 256,000 (the routed envelope cap), B = 65,536 (dsh's default): // threshold = min(floor(W × 0.8), W − O − B) = min(800,000, 678,464). const reserve = reserveGeometry(1_000_000, 256_000) assert.deepEqual(reserve, { threshold: 678_464, headroom: COMPACT_HEADROOM_TOKENS, output: 256_000 }) - assert.ok(tiles(reserve!, 1_000_000)) + assert.ok(triggerAtOrLeftOfMasks(reserve!, 1_000_000)) }) - test('a small output cap leaves the ratio binding, and the headroom mask grows to the ratio slack', () => { - // threshold = min(800,000, 926,464) = 800,000; the mask runs to the output reservation's edge. + test('a small output cap leaves the ratio binding: the trigger detaches, the headroom keeps its true size', () => { + // threshold = min(800,000, 926,464) = 800,000 — strictly left of the masks' 926,464 edge. const reserve = reserveGeometry(1_000_000, 8_000) - assert.deepEqual(reserve, { threshold: 800_000, headroom: 192_000, output: 8_000 }) + assert.deepEqual(reserve, { threshold: 800_000, headroom: COMPACT_HEADROOM_TOKENS, output: 8_000 }) assert.equal(reserve!.threshold, Math.floor(1_000_000 * COMPACT_THRESHOLD_RATIO)) - assert.ok(tiles(reserve!, 1_000_000)) + assert.ok(triggerAtOrLeftOfMasks(reserve!, 1_000_000)) }) - test('an unknown, non-positive, or non-finite output cap reserves nothing', () => { - // No output reservation, so the ratio binds at 800,000 and the headroom mask is the rest. - const expected = { threshold: 800_000, headroom: 200_000, output: 0 } + test('an unknown, non-positive, or non-finite output cap reserves no output', () => { + // No output reservation, so the ratio binds at 800,000 and the headroom mask stands alone. + const expected = { threshold: 800_000, headroom: COMPACT_HEADROOM_TOKENS, output: 0 } for (const cap of [undefined, 0, -8_000, Number.NaN, Number.POSITIVE_INFINITY] as const) { assert.deepEqual(reserveGeometry(1_000_000, cap), expected, String(cap)) } @@ -58,6 +60,6 @@ describe('reserveGeometry', () => { // One token of budget survives, so the masks come back — the headroom is exactly dsh's default. const tight = reserveGeometry(65_537, 0) assert.deepEqual(tight, { threshold: 1, headroom: COMPACT_HEADROOM_TOKENS, output: 0 }) - assert.ok(tiles(tight!, 65_537)) + assert.ok(triggerAtOrLeftOfMasks(tight!, 65_537)) }) })