From de856c4aa719fcaa38d2fffa8247298694a3d2a0 Mon Sep 17 00:00:00 2001 From: Esther-2584 Date: Fri, 30 Oct 2026 14:19:42 -0400 Subject: [PATCH 1/5] test(compression): a merge cut off, or whose call failed, keeps its place behind the L1s The relief rule (mergeTakesTurnByRelief) keeps a merge that has failed or been refused before behind the queued L1s, but two of its checks had no case that fails without them: - `(head.attempts ?? 0) > 0`: the refused-once case also sets hadRefusal, which covers it alone. A merge cut off at max_tokens now has a case. - `this.mergeFailureOrder.has(key)`: a merge whose call failed is still at the head of the queue when the lane's pause ends, and without the check it goes first again. A case sets the pause's backoff to 0 and ticks once more. Both run on openOrder, which gains truncateMerges and failFirstMerge. The new cases, and the refused-once case beside them, assert that the merge is still queued after its failed call. Each new case fails with its check removed, and passes at 01f3a851. --- test/liveness-floor.test.ts | 30 +++++++++++++++++++++++++++++- 1 file changed, 29 insertions(+), 1 deletion(-) diff --git a/test/liveness-floor.test.ts b/test/liveness-floor.test.ts index 0b2b8b51..6b0fa66a 100644 --- a/test/liveness-floor.test.ts +++ b/test/liveness-floor.test.ts @@ -263,15 +263,21 @@ class OrderProbe extends AutobiographicalStrategy { } } -async function openOrder(opts: { adaptive?: boolean; refuseMerges?: boolean; drainMode?: boolean } = {}) { +async function openOrder(opts: { adaptive?: boolean; refuseMerges?: boolean; truncateMerges?: boolean; failFirstMerge?: boolean; drainMode?: boolean } = {}) { cleanup(); const calls: Array<'l1' | 'merge'> = []; + let mergeFailed = false; const membrane = { complete: async (request: { messages?: unknown[] }) => { const last = JSON.stringify(request.messages?.[request.messages.length - 1] ?? ''); const merge = /consolidat/i.test(last); calls.push(merge ? 'merge' : 'l1'); if (merge && opts.refuseMerges) return { stopReason: 'refusal', content: [], usage: { input_tokens: 100, output_tokens: 0 } }; + if (merge && opts.truncateMerges) return { stopReason: 'max_tokens', content: [t(`a recollection cut off ${words(2)}`)], usage: { input_tokens: 100, output_tokens: 40 } }; + if (merge && opts.failFirstMerge && !mergeFailed) { + mergeFailed = true; + throw Object.assign(new Error('connection reset'), { type: 'network', retryable: true }); + } return { stopReason: 'end_turn', content: [t(`a recollection of what happened, ${words(4)}`)], usage: { input_tokens: 100, output_tokens: 40 } }; }, }; @@ -325,11 +331,33 @@ describe('The relief rule leaves the usual order where it should (#415)', () => it('runs the next L1 before a merge the model has refused once', async () => { const probe = await openOrder({ refuseMerges: true }); await probe.tickOnce(); + assert.strictEqual(probe.strategy.getProgressSnapshot().mergeQueueLength, 1, 'premise: the refused merge is still queued'); await probe.tickOnce(); assert.deepStrictEqual(probe.calls, ['merge', 'l1'], 'the refused merge is retried only behind the queued L1s'); probe.manager.close(); }); + it('runs the next L1 before a merge whose answer was cut off', async () => { + const probe = await openOrder({ truncateMerges: true }); + await probe.tickOnce(); + assert.strictEqual(probe.strategy.getProgressSnapshot().mergeQueueLength, 1, 'premise: the cut-off merge is still queued'); + await probe.tickOnce(); + assert.deepStrictEqual(probe.calls, ['merge', 'l1'], 'the cut-off merge is retried only behind the queued L1s'); + probe.manager.close(); + }); + + it('runs the next L1 before a merge whose call failed, once the pause it caused has passed', async () => { + const probe = await openOrder({ failFirstMerge: true }); + await assert.rejects(() => probe.tickOnce(), /connection reset/); + const lane = probe.strategy as unknown as { compressionPause: { backoffUntil: number } | null }; + assert.ok(lane.compressionPause, 'premise: the failure paused the lane'); + assert.strictEqual(probe.strategy.getProgressSnapshot().mergeQueueLength, 1, 'premise: the failed merge is still queued'); + lane.compressionPause.backoffUntil = 0; + await probe.tickOnce(); + assert.deepStrictEqual(probe.calls, ['merge', 'l1'], 'the failed merge is retried only behind the queued L1s'); + probe.manager.close(); + }); + it('runs the next L1 first under the drain script (landsAsPlanned)', async () => { const probe = await openOrder({ drainMode: true }); await probe.tickOnce(); From 1ff043fd16ce3ff661c0fc48af6d00c9dc90a514 Mon Sep 17 00:00:00 2001 From: Esther-2584 Date: Fri, 30 Oct 2026 14:19:42 -0400 Subject: [PATCH 2/5] fix(render-stats): outside kv-unified the floor counts no lock The floor builds kv-unified's constraint model under every folding strategy. The other folding strategies give pins precedence over locks in ways that model doesn't follow: a classic pin overrides a lock (lockChunk's doc), and under kv-stable a level pin holds its summary's group at its level, a lock under it included. So a message locked at one level and pinned at another left the floor missing while every compile went on, and a lock that a sibling's level pin overrode through their shared summary raised the floor above the plan kv-stable makes. - floorTokens: under kv-unified the floor still takes that solver's constraints, locks included, and is exact. Under the other folding strategies it counts no lock, so it is a lower bound there: a floor over the budget still means every compile refuses. - initialize clears the last floor failure's cause, beside the strategy's other once-per-load logs, so a store opened again says a failure that lasts. - Cases: under kv-stable, flat-profile and oldest-first, a raw pin over a locked, folded message keeps the floor, at or under the plan, and raises it; under kv-stable, a lock that a sibling's level pin overrides leaves the floor as it is, and a level pin over a message locked raw keeps it, where kv-unified refuses the compile for the same pair; under kv-unified a lock that holds a message raw raises the floor; a failing computation is said once, again for another cause, again when that cause recurs after the floor came back, and again for the store opened anew. Each case fails at 01f3a851 but the kv-unified one, which pins the floor as it stands there, and each fails with the change it covers removed. --- src/strategies/autobiographical.ts | 18 +++- test/liveness-floor.test.ts | 153 ++++++++++++++++++++++++++++- 2 files changed, 163 insertions(+), 8 deletions(-) diff --git a/src/strategies/autobiographical.ts b/src/strategies/autobiographical.ts index 56dfa7ed..002ea4fd 100644 --- a/src/strategies/autobiographical.ts +++ b/src/strategies/autobiographical.ts @@ -3088,6 +3088,7 @@ export class AutobiographicalStrategy implements ResettableStrategy { // failure only an earlier one met. A host that moves the store's branch // itself leaves this load in place until the manager initializes it again. this.namedDroppedQuarantineKeys.clear(); + this.floorFailure = undefined; this.namedUnlistedQuarantineState.clear(); this.compressionDebtFailureLogged = null; @@ -6379,17 +6380,24 @@ export class AutobiographicalStrategy implements ResettableStrategy { * The floor (#415): the cheapest plan the summaries held allow for these * inputs, head and tail included, in the plan's units. An adaptive compile * refuses once it passes the hard budget, whatever the folding strategy - * picks, so the distance between the two is how near the store is to its - * first refusal. Null where the forest can't be built for these inputs; - * the figure is a report and never fails a compile. + * picks, and every plan does once the floor does, so the distance between + * the two is how near the store is to the point where every compile + * refuses. Under kv-unified it takes that solver's own constraints, locks + * included. The other folding strategies give pins precedence over locks + * in ways these constraints don't follow, so there it counts no lock, and + * is a lower bound. Null where the forest can't be built for these inputs or + * the computation fails; the figure is a report and never fails a compile. */ private floorTokens(inputs: PickerInputs): number | null { try { const kv = this.config.foldingStrategy === 'kv-unified' ? this.config.kvUnified : undefined; const reused = kv ? this.kvUnifiedReuse.forest : undefined; - const forest = reused?.builtFrom(inputs) + const floorInputs = this.config.foldingStrategy === 'kv-unified' + ? inputs + : { ...inputs, chunks: inputs.chunks.map((chunk) => chunk.lockedByAgent ? { ...chunk, lockedByAgent: false } : chunk) }; + const forest = reused?.builtFrom(floorInputs) ? reused - : new CanonicalSummaryForest(inputs, { + : new CanonicalSummaryForest(floorInputs, { treeifyNonContiguousSummaries: kv?.treeifyNonContiguousSummaries, preserveGapBearingSummaries: kv?.preserveGapBearingSummaries, }); diff --git a/test/liveness-floor.test.ts b/test/liveness-floor.test.ts index 6b0fa66a..bdf82210 100644 --- a/test/liveness-floor.test.ts +++ b/test/liveness-floor.test.ts @@ -16,15 +16,19 @@ * refused at its deepest available level (#412). * - Through bursts the lane can't keep up with, followed by idle stretches * that let it catch up, the compile stays live. - * - Each adaptive compile reports its floor beside the hard budget, and the - * floor drops as relief lands; a refusal names its floor and the relief - * the queue holds. + * - Each adaptive compile whose floor can be computed reports it beside the + * hard budget, and the floor drops as relief lands; a refusal after the + * plan names its floor and the relief the queue holds, when the floor was + * computed. Outside kv-unified the floor counts no lock, so a lock a pin + * overrides doesn't take it away; under kv-unified it counts every lock. A + * floor that can't be computed is said once per cause. */ import { describe, it, after } from 'node:test'; import assert from 'node:assert'; import { rmSync, existsSync } from 'node:fs'; import { ContextManager, AutobiographicalStrategy, OverBudgetError } from '../src/index.js'; +import { CanonicalSummaryForest } from '../src/adaptive/kv-unified.js'; import type { ContentBlock } from '@animalabs/membrane'; const STORE = './test-liveness-floor'; @@ -74,6 +78,13 @@ interface Reader { const settle = async () => { for (let i = 0; i < 5; i++) await new Promise((r) => setImmediate(r)); }; +function collectFloorWarnings(): { warnings: string[]; restore: () => void } { + const warnings: string[] = []; + const warn = console.warn; + console.warn = (message?: unknown) => { if (/floor not computed/.test(String(message))) warnings.push(String(message)); }; + return { warnings, restore: () => { console.warn = warn; } }; +} + async function openReader(config: Record = {}, summaryWords = 4, refuse?: RegExp): Promise { cleanup(); const calls = { l1: 0, merge: 0 }; @@ -247,6 +258,142 @@ describe('Adaptive compile liveness against the floor (#415)', () => { assert.match(refusal.message, new RegExp(`floor=${floorTokens}, queued relief=${queuedReliefTokens}`)); reader.manager.close(); }); + + for (const foldingStrategy of ['kv-stable', 'flat-profile', 'oldest-first']) { + it(`keeps the floor where a pin overrides a lock, under ${foldingStrategy}`, async () => { + const reader = await openReader({ foldingStrategy, kvUnified: undefined }); + reader.read(40); + await reader.drain(); + await reader.manager.compile({ maxTokens: 3_000, reserveForResponse: 0 }); + const resolutions = (reader.strategy as unknown as { resolutions: Map }).resolutions; + const folded = [...resolutions].find(([, level]) => level >= 1)?.[0]; + assert.ok(folded, 'premise: a message is folded'); + const budget = { maxTokens: 20_000, reserveForResponse: 0 }; + const floor = collectFloorWarnings(); + try { + reader.strategy.lockChunk(folded); + await reader.manager.compile(budget); + const beforePin = reader.manager.getRenderStats()?.planVsActual?.floorTokens; + reader.manager.pinRange(folded, folded); + await reader.manager.compile(budget); + const stats = reader.manager.getRenderStats()?.planVsActual; + assert.ok(stats?.floorTokens !== undefined, 'the floor is reported beside a raw pin over a locked message'); + assert.ok(stats.floorTokens <= stats.planned, `at or under the plan (${stats.floorTokens} > ${stats.planned})`); + assert.ok(beforePin !== undefined && stats.floorTokens > beforePin, `the pin counts (${stats.floorTokens}, ${beforePin})`); + assert.strictEqual(floor.warnings.length, 0, floor.warnings.join('\n')); + } finally { + floor.restore(); + } + reader.manager.close(); + }); + } + + it('counts no lock outside kv-unified, so a lock a level pin overrides through its summary leaves the floor under the plan', async () => { + const reader = await openReader({ foldingStrategy: 'kv-stable', kvUnified: undefined }); + reader.read(40); + await reader.drain(); + const locked = reader.manager.getAllMessages()[20]!.id; + reader.strategy.lockChunk(locked); + await reader.manager.compile({ maxTokens: 3_000, reserveForResponse: 0 }); + const summaries = (reader.strategy as unknown as { summaries: { level: number; sourceIds: string[] }[] }).summaries; + const sibling = summaries.find((summary) => summary.level === 1 && summary.sourceIds.includes(locked))?.sourceIds.find((id) => id !== locked); + assert.ok(sibling, 'premise: the locked message shares an L1'); + const budget = { maxTokens: 20_000, reserveForResponse: 0 }; + reader.manager.pinRange(sibling, sibling, { level: 1 }); + await reader.manager.compile(budget); + const withLock = reader.manager.getRenderStats()?.planVsActual; + reader.strategy.unlockChunk(locked); + await reader.manager.compile(budget); + const withoutLock = reader.manager.getRenderStats()?.planVsActual; + assert.ok(withLock?.floorTokens !== undefined && withLock.floorTokens <= withLock.planned, `at or under the plan (${withLock?.floorTokens}, ${withLock?.planned})`); + assert.strictEqual(withLock.floorTokens, withoutLock?.floorTokens, 'the lock leaves the floor as it is'); + reader.manager.close(); + }); + + it('counts a lock under kv-unified, whose constraints the floor takes', async () => { + const reader = await openReader(); + reader.read(40); + await reader.drain(); + const budget = { maxTokens: 20_000, reserveForResponse: 0 }; + const floorNow = async () => { + await reader.manager.compile(budget); + return reader.manager.getRenderStats()?.planVsActual?.floorTokens; + }; + const free = await floorNow(); + reader.strategy.lockChunk(reader.manager.getAllMessages()[20]!.id); + const held = await floorNow(); + assert.ok(free !== undefined && held !== undefined && held > free, `a message held raw raises the floor (${held}, ${free})`); + reader.manager.close(); + }); + + it('keeps the floor where a level pin overrides a lock under kv-stable, as kv-unified refuses the compile for it', async () => { + const budget = { maxTokens: 20_000, reserveForResponse: 0 }; + const unified = await openReader(); + unified.read(40); + await unified.drain(); + const first = unified.manager.getAllMessages()[0]!.id; + unified.strategy.lockChunk(first); + unified.manager.pinRange(first, first, { level: 2 }); + await assert.rejects(unified.manager.compile(budget), /infeasible/, 'premise: under kv-unified the lock and the pin conflict'); + unified.manager.close(); + + const reader = await openReader({ foldingStrategy: 'kv-stable', kvUnified: undefined }); + reader.read(40); + await reader.drain(); + const id = reader.manager.getAllMessages()[0]!.id; + const floor = collectFloorWarnings(); + try { + reader.strategy.lockChunk(id); + reader.manager.pinRange(id, id, { level: 2 }); + await reader.manager.compile(budget); + const stats = reader.manager.getRenderStats()?.planVsActual; + assert.ok(stats?.floorTokens !== undefined, 'the floor is reported'); + assert.ok(stats.floorTokens <= stats.planned, `at or under the plan (${stats.floorTokens} > ${stats.planned})`); + assert.strictEqual(floor.warnings.length, 0, floor.warnings.join('\n')); + } finally { + floor.restore(); + } + reader.manager.close(); + }); + + it('says once that a floor whose computation fails is missing, again when it recurs or the store is opened again, and the compile goes on', async () => { + const reader = await openReader({ foldingStrategy: 'kv-stable', kvUnified: undefined }); + reader.read(24); + await reader.drain(); + const budget = { maxTokens: 20_000, reserveForResponse: 0 }; + const forest = CanonicalSummaryForest.prototype as unknown as { minimumTokens: () => unknown }; + const minimumTokens = forest.minimumTokens; + const breakIt = () => { forest.minimumTokens = () => { throw new Error('the forest broke'); }; }; + const floor = collectFloorWarnings(); + try { + breakIt(); + await reader.manager.compile(budget); + await reader.manager.compile(budget); + const stats = reader.manager.getRenderStats()?.planVsActual; + assert.ok(stats, 'premise: the compile went on'); + assert.strictEqual(stats.floorTokens, undefined, 'no floor is reported'); + assert.strictEqual(stats.hardBudget, undefined, 'nor the budget beside it'); + assert.strictEqual(floor.warnings.length, 1, `said once over two compiles (${floor.warnings.length})`); + assert.match(floor.warnings[0]!, /floor not computed.*the forest broke/); + forest.minimumTokens = () => { throw new Error('the forest broke otherwise'); }; + await reader.manager.compile(budget); + assert.strictEqual(floor.warnings.length, 2, `another cause is said too (${floor.warnings.length})`); + forest.minimumTokens = minimumTokens; + await reader.manager.compile(budget); + assert.ok(reader.manager.getRenderStats()?.planVsActual?.floorTokens !== undefined, 'premise: the floor is back'); + forest.minimumTokens = () => { throw new Error('the forest broke otherwise'); }; + await reader.manager.compile(budget); + assert.strictEqual(floor.warnings.length, 3, `said again when it recurs (${floor.warnings.length})`); + reader.manager.close(); + const reopened = await ContextManager.open({ path: STORE, strategy: reader.strategy, membrane: { complete: async () => { throw new Error('no call expected'); } } as never }); + await reopened.compile(budget); + assert.strictEqual(floor.warnings.length, 4, `said again for the store opened anew (${floor.warnings.length})`); + reopened.close(); + } finally { + forest.minimumTokens = minimumTokens; + floor.restore(); + } + }); }); // The relief rule's exclusions: where the merge at the head keeps its usual From ff91ff90dd8081910d1a451f69ab92c21455eb64 Mon Sep 17 00:00:00 2001 From: Esther-2584 Date: Fri, 30 Oct 2026 14:19:43 -0400 Subject: [PATCH 3/5] docs(render-stats): say what the floor promises, and when it's absent - The fragment and RenderStats.planVsActual.floorTokens: `hardBudget - floorTokens` is how far the store is from the point where every compile refuses, since a refusal can come earlier; it usually shrinks as history arrives. Outside kv-unified the floor counts no lock and is a lower bound. A floor that can't be computed is said once per cause, and once more by each load that meets it. - OverBudgetDiagnostics: the floor comes on the adaptive refusals made after the plan, when it was computed; the refusal for a head and tail that alone exceed the budget comes first and carries neither field. At or under the budget a plan fits, outside kv-unified unless a lock holds a message above its cheapest form. Queued relief is an estimate, and fewer messages or a released pin or lock can lower the floor too. --- changelog.d/415-floor-headroom.added.md | 29 +++++++++++++++++-------- src/adaptive/picker.ts | 16 +++++++++----- src/types/strategy.ts | 12 ++++++---- 3 files changed, 39 insertions(+), 18 deletions(-) diff --git a/changelog.d/415-floor-headroom.added.md b/changelog.d/415-floor-headroom.added.md index 687a061b..3ea5e389 100644 --- a/changelog.d/415-floor-headroom.added.md +++ b/changelog.d/415-floor-headroom.added.md @@ -3,18 +3,29 @@ `hardBudget=` and `queuedRelief=` on the `[plan-vs-actual]` line. The floor is the cheapest plan the summaries held allow, head and tail included, whatever the folding strategy picked; a compile refuses once its - plan passes `hardBudget`, so `hardBudget - floorTokens` is how far the store - is from its first refusal. `queuedReliefTokens` is what the queued L1s and + plan passes `hardBudget`, and every plan does once the floor does, so + `hardBudget - floorTokens` is how far the store is from the point where + every compile refuses. `queuedReliefTokens` is what the queued L1s and merges are expected to take off the floor once they land. Work tick won't turn into a summary counts as zero: a chunk the compression quarantine holds or deferred for the host's tools, every chunk from the first a compression hold reaches, a merge holding a source a hold or a merge-quarantine record keeps out, and every merge from the first a hold - blocks. A floor that can't be computed leaves the fields out and is said - once per cause, as `[autobiographical] floor not computed`. + blocks. Under `kv-unified` the floor takes that solver's own constraints, + locks included, and is exact. The other folding strategies give pins + precedence over locks in ways those constraints don't follow, so there the floor + counts no lock and is a lower bound: over the budget, every compile still + refuses; at or under it, a plan fits only where no lock holds a message + above its cheapest form. A floor that can't be computed leaves the fields + out and is said once per cause, and once more by each load that meets it, + as `[autobiographical] floor not computed`. - `OverBudgetDiagnostics` gains `floorTokens` and `queuedReliefTokens` on - adaptive refusals, and the refusal's message ends with `floor=` and - `queued relief=`. A floor at or under the budget means the refusal isn't - for want of summaries, since a plan that fits exists; a floor still over - the budget once the queued relief is taken off means draining the queue - alone won't clear it. + the adaptive refusals that come once the plan is made, when its floor was + computed, and the refusal's message then names them as `floor=` and + `queued relief=`. The refusal for a head and tail that alone exceed the + budget comes before the plan, so it carries neither. A floor at or under + the budget means the refusal isn't for want of summaries, since a plan + that fits exists (outside `kv-unified`, unless a lock holds a message + above its cheapest form); a floor still over the budget once the queued + relief is taken off means draining the queue alone isn't expected to clear + it, the relief being an estimate. diff --git a/src/adaptive/picker.ts b/src/adaptive/picker.ts index 3f0226ca..a1531add 100644 --- a/src/adaptive/picker.ts +++ b/src/adaptive/picker.ts @@ -64,16 +64,22 @@ export interface OverBudgetDiagnostics { rawPins?: { tokens: number; pins: readonly OverBudgetRawPin[] }; /** * Under adaptive resolution, the cheapest plan the summaries held allow, - * head and tail included (#415). At or under the budget, a plan that could - * fit wasn't picked; over it, only new summaries, a larger budget or a - * smaller tail clear the refusal. Absent where it couldn't be computed. + * head and tail included (#415), counting no lock outside kv-unified, + * where it's a lower bound. At or under the budget, a plan that fits + * exists, outside kv-unified unless a lock holds a message above its + * cheapest form, so the refusal isn't for want of summaries; over it, no + * plan fits until the floor drops under the budget or the budget rises, as + * new summaries, a smaller tail, fewer messages, or a released pin or lock + * that kept a message from its cheapest form can bring about. Absent where + * it couldn't be computed, and on a refusal that comes before the plan, as + * the head and tail reservation's does. */ floorTokens?: number; /** * Under adaptive resolution, what the queued compression work is expected * to take off `floorTokens` once it lands (RenderStats' `queuedReliefTokens`). - * A floor that stays over the budget less this much won't be cleared by - * draining the queue alone. + * It's an estimate, so a floor that stays over the budget less this much + * isn't expected to be cleared by draining the queue alone. */ queuedReliefTokens?: number; } diff --git a/src/types/strategy.ts b/src/types/strategy.ts index 67ecabfe..d0e9c099 100644 --- a/src/types/strategy.ts +++ b/src/types/strategy.ts @@ -1143,10 +1143,14 @@ export interface RenderStats { /** The floor (#415): the cheapest plan the summaries held allow for this * compile's inputs, head and tail included, in the plan's units, whatever * the folding strategy picked. A compile refuses once its plan passes - * `hardBudget`, so `hardBudget - floorTokens` is how far the store is - * from its first refusal: it shrinks as history arrives and grows as - * L1s and merges land. Absent where the forest couldn't be built - * (`floor=` on the plan line). */ + * `hardBudget`, and every plan does once the floor does, so + * `hardBudget - floorTokens` is how far the store is from the point + * where every compile refuses: it usually shrinks as history arrives and + * grows as L1s and merges land. Under kv-unified it takes that solver's + * own constraints, locks included; under the other folding strategies, + * which give pins precedence over locks, it counts no lock, and is a + * lower bound. Absent where the forest couldn't be built or its computation + * fails (`floor=` on the plan line). */ floorTokens?: number; /** The plan size above which this compile refuses: the input budget with * `overBudgetGraceRatio` added (`hardBudget=`). Set with `floorTokens`. */ From 9ddd0ddbdfb8fddbaa43d69362329424aa8f3ee1 Mon Sep 17 00:00:00 2001 From: Esther-2584 Date: Fri, 30 Oct 2026 14:22:19 -0400 Subject: [PATCH 4/5] docs(render-stats): outside kv-unified the floor promises no fit A floor at or under the budget doesn't promise a plan fits outside kv-unified: besides locks, kv-stable holds every message under a level pin's summary at the pin's level, which the floor's constraints don't follow either. The fragment, the refusal paragraph and OverBudgetDiagnostics.floorTokens now say only that: a floor over the budget means every compile refuses, and one at or under it doesn't promise a plan fits. --- changelog.d/415-floor-headroom.added.md | 25 +++++++++++++------------ src/adaptive/picker.ts | 19 ++++++++++--------- 2 files changed, 23 insertions(+), 21 deletions(-) diff --git a/changelog.d/415-floor-headroom.added.md b/changelog.d/415-floor-headroom.added.md index 3ea5e389..d03ccc6a 100644 --- a/changelog.d/415-floor-headroom.added.md +++ b/changelog.d/415-floor-headroom.added.md @@ -13,19 +13,20 @@ merge-quarantine record keeps out, and every merge from the first a hold blocks. Under `kv-unified` the floor takes that solver's own constraints, locks included, and is exact. The other folding strategies give pins - precedence over locks in ways those constraints don't follow, so there the floor - counts no lock and is a lower bound: over the budget, every compile still - refuses; at or under it, a plan fits only where no lock holds a message - above its cheapest form. A floor that can't be computed leaves the fields - out and is said once per cause, and once more by each load that meets it, - as `[autobiographical] floor not computed`. + precedence over locks in ways those constraints don't follow, so there the + floor counts no lock. Outside `kv-unified` the floor is a lower bound: a + floor over the budget means every compile refuses, and one at or under it + doesn't promise a plan fits. A floor that can't be computed leaves the + fields out and is said once per cause, and once more by each load that + meets it, as `[autobiographical] floor not computed`. - `OverBudgetDiagnostics` gains `floorTokens` and `queuedReliefTokens` on the adaptive refusals that come once the plan is made, when its floor was computed, and the refusal's message then names them as `floor=` and `queued relief=`. The refusal for a head and tail that alone exceed the - budget comes before the plan, so it carries neither. A floor at or under - the budget means the refusal isn't for want of summaries, since a plan - that fits exists (outside `kv-unified`, unless a lock holds a message - above its cheapest form); a floor still over the budget once the queued - relief is taken off means draining the queue alone isn't expected to clear - it, the relief being an estimate. + budget comes before the plan, so it carries neither. Under `kv-unified`, + a floor at or under the budget means the refusal isn't for want of + summaries, since a plan that fits exists. Outside `kv-unified` the floor is + a lower bound: a floor over the budget means every compile refuses, and one + at or under it doesn't promise a plan fits. A floor still over the budget + once the queued relief is taken off means draining the queue alone isn't + expected to clear it, the relief being an estimate. diff --git a/src/adaptive/picker.ts b/src/adaptive/picker.ts index a1531add..07dff634 100644 --- a/src/adaptive/picker.ts +++ b/src/adaptive/picker.ts @@ -64,15 +64,16 @@ export interface OverBudgetDiagnostics { rawPins?: { tokens: number; pins: readonly OverBudgetRawPin[] }; /** * Under adaptive resolution, the cheapest plan the summaries held allow, - * head and tail included (#415), counting no lock outside kv-unified, - * where it's a lower bound. At or under the budget, a plan that fits - * exists, outside kv-unified unless a lock holds a message above its - * cheapest form, so the refusal isn't for want of summaries; over it, no - * plan fits until the floor drops under the budget or the budget rises, as - * new summaries, a smaller tail, fewer messages, or a released pin or lock - * that kept a message from its cheapest form can bring about. Absent where - * it couldn't be computed, and on a refusal that comes before the plan, as - * the head and tail reservation's does. + * head and tail included (#415). Under kv-unified, at or under the budget a + * plan that fits exists, so the refusal isn't for want of summaries. + * Outside kv-unified the floor counts no lock and is a lower bound: a floor + * over the budget means every compile refuses, and one at or under it + * doesn't promise a plan fits. Over the budget, no plan fits until the + * floor drops under the budget or the budget rises, as new summaries, a + * smaller tail, fewer messages, or a released pin or lock that kept a + * message from its cheapest form can bring about. Absent where it couldn't + * be computed, and on a refusal that comes before the plan, as the head + * and tail reservation's does. */ floorTokens?: number; /** From 91074915294b0b62ae68aeb59d3d734b5ba28ddf Mon Sep 17 00:00:00 2001 From: Esther-2584 Date: Fri, 30 Oct 2026 15:15:12 -0400 Subject: [PATCH 5/5] docs(render-stats): name hardBudget where the floor is tied to a refusal A compile refuses only once its plan passes hardBudget, the input budget with overBudgetGraceRatio added, and OverBudgetDiagnostics carries two budgets. A floor between the input budget and hardBudget leaves room for plans that don't refuse, so "a floor over the budget means every compile refuses" was false under the input-budget reading. Name hardBudget in each sentence that ties the floor to a refusal or to what draining the queue can clear, and say in OverBudgetDiagnostics' floor doc what hardBudget is. The kv-unified sentences, that a plan fits at or under the budget, hold for either budget and stay as they were. --- changelog.d/415-floor-headroom.added.md | 10 +++++----- src/adaptive/picker.ts | 14 ++++++++------ 2 files changed, 13 insertions(+), 11 deletions(-) diff --git a/changelog.d/415-floor-headroom.added.md b/changelog.d/415-floor-headroom.added.md index d03ccc6a..54a8b06e 100644 --- a/changelog.d/415-floor-headroom.added.md +++ b/changelog.d/415-floor-headroom.added.md @@ -15,7 +15,7 @@ locks included, and is exact. The other folding strategies give pins precedence over locks in ways those constraints don't follow, so there the floor counts no lock. Outside `kv-unified` the floor is a lower bound: a - floor over the budget means every compile refuses, and one at or under it + floor over `hardBudget` means every compile refuses, and one at or under it doesn't promise a plan fits. A floor that can't be computed leaves the fields out and is said once per cause, and once more by each load that meets it, as `[autobiographical] floor not computed`. @@ -26,7 +26,7 @@ budget comes before the plan, so it carries neither. Under `kv-unified`, a floor at or under the budget means the refusal isn't for want of summaries, since a plan that fits exists. Outside `kv-unified` the floor is - a lower bound: a floor over the budget means every compile refuses, and one - at or under it doesn't promise a plan fits. A floor still over the budget - once the queued relief is taken off means draining the queue alone isn't - expected to clear it, the relief being an estimate. + a lower bound: a floor over `hardBudget` means every compile refuses, and + one at or under it doesn't promise a plan fits. A floor still over + `hardBudget` once the queued relief is taken off means draining the queue + alone isn't expected to clear it, the relief being an estimate. diff --git a/src/adaptive/picker.ts b/src/adaptive/picker.ts index 07dff634..09c0f4f2 100644 --- a/src/adaptive/picker.ts +++ b/src/adaptive/picker.ts @@ -67,11 +67,13 @@ export interface OverBudgetDiagnostics { * head and tail included (#415). Under kv-unified, at or under the budget a * plan that fits exists, so the refusal isn't for want of summaries. * Outside kv-unified the floor counts no lock and is a lower bound: a floor - * over the budget means every compile refuses, and one at or under it - * doesn't promise a plan fits. Over the budget, no plan fits until the - * floor drops under the budget or the budget rises, as new summaries, a - * smaller tail, fewer messages, or a released pin or lock that kept a - * message from its cheapest form can bring about. Absent where it couldn't + * over `hardBudget` (`inputBudget` with `overBudgetGraceRatio` added, the + * plan size above which a compile refuses, as `RenderStats` reports it) + * means every compile refuses, and one at or under it doesn't promise a + * plan fits. Over `hardBudget`, no plan fits until the floor drops under it + * or the budget rises, as new summaries, a smaller tail, fewer messages, or + * a released pin or lock that kept a message from its cheapest form can + * bring about. Absent where it couldn't * be computed, and on a refusal that comes before the plan, as the head * and tail reservation's does. */ @@ -79,7 +81,7 @@ export interface OverBudgetDiagnostics { /** * Under adaptive resolution, what the queued compression work is expected * to take off `floorTokens` once it lands (RenderStats' `queuedReliefTokens`). - * It's an estimate, so a floor that stays over the budget less this much + * It's an estimate, so a floor that stays over `hardBudget` less this much * isn't expected to be cleared by draining the queue alone. */ queuedReliefTokens?: number;