fix(ui): clearing the context-window buffer removes it from the payload

Both review bots converged on the same defect: an emptied buffer field
early-returned in commitBuffer, the draft was discarded on blur, and the
stale number reappeared and stayed in the saved config, contradicting
the copy that an empty field tracks the backend default. An empty commit
now removes the key, which the managed-keys projection propagates as a
real deletion on edit. Also trims the narrative comments the review
flagged as restating behavior
This commit is contained in:
Tin Chi Lo 2026-08-31 19:40:48 -07:00
parent 3793366a43
commit d7da446183
4 changed files with 29 additions and 9 deletions

View file

@ -8,13 +8,16 @@ const ContextWindowEscalationConfig: React.FC<{
onChange: (value: ComplexityRouterConfigValue) => void;
}> = ({ value, onChange }) => {
const enabled = value.enable_context_window_escalation ?? true;
// A number input renders Number("0.") as "0", so a decimal cannot be typed without a local draft.
const [bufferDraft, setBufferDraft] = React.useState<string | null>(null);
// min/max are inert on a text input, and a plain number input renders Number("0.") as "0" so a
// decimal cannot be typed. Hence the local draft plus an explicit clamp on commit.
const commitBuffer = (raw: string) => {
setBufferDraft(null);
if (raw.trim() === "") {
onChange({ ...value, context_window_escalation_buffer: undefined });
return;
}
const parsed = Number(raw);
if (raw.trim() === "" || !Number.isFinite(parsed)) return;
if (!Number.isFinite(parsed)) return;
onChange({ ...value, context_window_escalation_buffer: Math.min(1, Math.max(0.01, parsed)) });
};
return (

View file

@ -394,8 +394,6 @@ describe("AddAutoRouterTab", () => {
});
});
// A plain number input renders Number("1.5") fine but the clamp is ours: values above 1 must commit as 1,
// and an untouched buffer must stay out of the payload so the router tracks the backend default.
it("clamps the context-window buffer to 1 and keeps an untouched buffer out of the payload", async () => {
const user = userEvent.setup();
vi.mocked(getMissingTiersError).mockReturnValue(null);
@ -417,6 +415,29 @@ describe("AddAutoRouterTab", () => {
expect(config).not.toHaveProperty("enable_context_window_escalation");
});
it("clearing the buffer removes it from the payload so the router tracks the backend default", async () => {
const user = userEvent.setup();
vi.mocked(getMissingTiersError).mockReturnValue(null);
renderWithProviders(<Harness />);
await user.type(screen.getByPlaceholderText(/smart_router/i), "ctx-clear-router");
expandDetailedConfiguration();
await user.click(screen.getByText("Advanced: Context Window Escalation"));
const buffer = await screen.findByLabelText("Window fit buffer");
fireEvent.change(buffer, { target: { value: "0.8" } });
fireEvent.blur(buffer, { target: { value: "0.8" } });
fireEvent.change(buffer, { target: { value: "" } });
fireEvent.blur(buffer, { target: { value: "" } });
await user.click(screen.getByRole("button", { name: /add auto router/i }));
await waitFor(() => expect(handleAddAutoRouterSubmit).toHaveBeenCalled());
expect(vi.mocked(handleAddAutoRouterSubmit).mock.calls.at(-1)?.[0].complexity_router_config).not.toHaveProperty(
"context_window_escalation_buffer",
);
});
// The scalar floor is the one scorer knob with no group dict behind it, so its wiring into the create
// payload is only proven end to end. 0 is the case a truthy check would silently drop.
it("carries a reasoning override floor of 0 through to the create payload", async () => {

View file

@ -59,8 +59,6 @@ describe("buildComplexityRouterConfig", () => {
expect(config).toEqual(expected);
});
// False is the value a truthy guard would silently drop, and it is the whole point of the toggle:
// an untouched form tracks the backend default (enabled), an explicit false is a real opt-out.
it("carries an explicit context-window escalation opt-out and buffer, false included", () => {
const config = buildComplexityRouterConfig({
...baseParams,

View file

@ -573,8 +573,6 @@ describe("autorouter_presets", () => {
expect(prefill.escalationKeywords).toEqual([]);
});
// The prefill mapping is a hand-written field list, so a new config key is silently dropped
// unless mapped; false is the value a `||` default would erase (the #38453 class).
it("carries a preset's context-window escalation opt-out and buffer through the prefill", () => {
const prefill = buildPresetPrefill(
{