Skip to content

fix(nudges): a configured threshold must never degrade to permissive - #636

Open
Mukller wants to merge 1 commit into
Tarquinen:devfrom
Mukller:fix/threshold-resolution
Open

Mukller wants to merge 1 commit into
Tarquinen:devfrom
Mukller:fix/threshold-resolution

Conversation

@Mukller

@Mukller Mukller commented Sep 28, 2026

Copy link
Copy Markdown

Closes #548

Problem

compress.minContextLimit accepts a percentage string, and a percentage can only become a token count once the host has reported the model context window. Until then resolveContextTokenLimit returned undefined, and isContextOverLimits collapsed that into:

const overMinLimit = minContextLimit === undefined ? true : currentTokens >= minContextLimit

A configured "15%" therefore behaved exactly like no threshold at all, and the turn/iteration nudges fired from the first request of a session. That is the reported symptom: compression starting at roughly 4% of the window with minContextLimit: "15%".

The same degradation happens when:

  • the value is unparseable ("not-a-number" yields NaN and falls through to undefined)
  • a per-model override in modelMinLimits exists but its key does not match the current provider/model

The upper limit already failed closed, which is why only the lower one misbehaved — the asymmetry is the tell.

Change

A limit that was configured is now distinguished from one that was never configured:

state overMinLimit
configured and evaluable compared against current usage, unchanged
configured but not evaluable false — no nudge is injected
never configured true — the existing permissive default

So a threshold DCP cannot evaluate yet fails closed, which is the behaviour the user asked for when they set it, and a typo in the config no longer silently enables compression.

Verification

tests/nudge-thresholds.test.ts:

  • minContextLimit: "15%" with the model context window not yet reported → overMinLimit === false
  • minContextLimit: "not-a-number" → overMinLimit === false
  • a resolvable threshold still compares correctly in both directions, and overMaxLimit is unaffected

The first two fail on the current implementation with percentage threshold silently became permissive.

Full suite 122 passing on this branch, tsc --noEmit clean, prettier --check clean.

Closes Tarquinen#548

compress.minContextLimit accepts a percentage string, and a percentage can only
be turned into a token count once the host has reported the model context
window. Until then resolveContextTokenLimit returned undefined, and
isContextOverLimits turned that into:

    const overMinLimit = minContextLimit === undefined ? true : ...

So a configured "15%" behaved exactly like no threshold at all, and the
turn/iteration nudges fired from the first request of a session. That is the
reported symptom: compression starting at roughly 4% of the window with
minContextLimit set to 15%. The same degradation happens for an unparseable
value, and for a per-model override (modelMinLimits) whose key does not match
the current provider/model.

The upper limit already failed closed, which is why only the lower one misbehaved.

Now a limit that was configured but could not be evaluated is distinguished from
a limit that was never configured:

- configured and evaluable -> compared against current usage, as before
- configured but not evaluable -> treated as not reached, so no nudge is injected
- never configured -> unchanged permissive default

Behaviour with a resolvable threshold is unchanged in both directions, which the
tests pin down.

Tests: tests/nudge-thresholds.test.ts, three cases. The first two fail on the
current implementation with `percentage threshold silently became permissive`.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant