feat: per-provider/per-model overrides for ALL compress fields via nested compress.providers (issue #344) - #351
Conversation
Growth-triggered compression nudges fired well below a configured minContextLimit because computeShouldNudge() only uses overMinLimit to pick the tips variant, not to gate the decision. Add an explicit (overMaxLimit || overMinLimit) gate to the T1 growth path in injectCompressNudges so a configured minimum acts as a lower bound. - overMaxLimit and the emergency override bypass the gate - T2/T3 tier-promotion nudges are unaffected (independent cadence) - 4 new tests; 2 existing growth-mechanism tests had minContextLimit lowered so they still isolate the growth mechanism devlog: devlog/2026-08-28_min-gate-growth-nudges/
- Gate only applies when minContextLimit resolves (minLimitResolved): percent limits with an unknown model context window keep pre-#342 growth-only behavior instead of suppressing all growth nudges - Unmask 2 existing tests whose minContextLimit sat above the test context (post-compress small growth, baseline init) - 3 new tests: unresolvable-limit fallback, emergency override bypass, T2 tier-promotion independence from the min gate - Correct stale min/max defaults in README.md and CONFIGURATION.md (45%/55% -> 80%/80%, matching lib/config.ts since v1.14.16) - Devlog: REQ constraints/acceptance, WORKLOG results (1036 tests)
…minContextLimit Per @Dog's feedback in issue #342: minContextLimit defaults to 80% (lib/config.ts:200) and is documented as the soft lower threshold for turn/iteration reminders, so gating growth nudges on it suppresses ALL growth nudges below 80% for default users — effectively disabling compression for most of a session. The dual-agent review's minLimitResolved guard did not address this (a default user with a known context still gets growth nudges suppressed below 80%). Use minNudgeContextPercent (default 15, a percent of model context) as the growth-nudge floor instead. It is the intended 'don't nudge below this' knob and was previously a no-op (plumbed into computeShouldNudge but ignored). When the model context limit is unknown the floor is unresolvable and growth nudges keep pre-#342 behavior. overMaxLimit and the emergency override bypass the floor; T2/T3 tier-promotion nudges are unaffected. - Reverts the review commit's minLimitResolved mechanism (dead code here) - Preserves the review commit's README/CONFIGURATION default fixes (80%/80%) - Tests: 4 floor tests + 2 new (unresolvable model limit, T2 independence) re-pointed at the floor; pre-existing test #27 raised into the [floor, max) range. 1035 tests, 0 failures.
…th-floor redesign Re-point stale references left by the floor redesign (ecfe7f0): - README.md: minContextLimit governs turn/iteration reminders only; growth nudges have their own floor (minNudgeContextPercent) - CONFIGURATION.md: minContextLimit description (turn/iteration only); minNudgeContextPercent description (growth-nudge floor, over-max and emergency bypasses, unknown-window fallback) - lib/messages/inject/utils.ts: fix the @deprecated comment on minNudgeContextPercent (field is active; floor computed in inject.ts) - tests/inject.test.ts: section header re-pointed to the floor - WORKLOG: record ecfe7f0 hash + this commit's row
A 15% default floor binds on windows >= ~400K (P% x W > baseline + 50K growth threshold): on 1M-window models every compress cycle is suppressed until 150K context usage, shifting the steady-state working range ~2x upward. For the dominant large-window user profile (baseline 10-50K, cycle ceiling ~110K) this is a silent, bug-level behavior change. 5% only binds on >= ~2M-class windows, keeping the floor inert for typical working cycles. Users who want a higher floor set it explicitly (0 disables). - lib/config.ts default 15 -> 5 - lib/messages/inject/inject.ts policy passthrough + floor fallback 15 -> 5, with rationale comment - dcp.schema.json default + stale description fixed - CONFIGURATION.md / CONFIGURATION.zh-CN.md defaults + rationale - tests/inject.test.ts: default-lock test (mutation-sensitive: fails if the fallback reverts to 15)
…mantics The zh description still said 'any nudge' (pre-floor-redesign wording); mirror the English entry: growth-nudge floor, over-max/emergency bypasses, unknown-window fallback, minContextLimit governs turn/iteration reminders, low-default rationale, 0 disables.
…roviders (issue #344) Replaces the flat modelMinNudgeLimits surface (PR #345, superseded) with compress.providers.{provider}.models.{model}.minNudgeContextPercent, cascading field-by-field: model > provider > global. 0 disables explicitly; unknown ids fall back to global; percent resolves against the active model window. Deep-merged across the three config file layers per provider/model key. Validated + schema + CONFIGURATION (en/zh) + devlog + 17 new tests (mutation-verified M1/M2/M3). 1053/1053 green.
📦 Built Plugin ArtifactBranch: Option A — Install from npm PR tag (recommended)opencode plugin opencode-acp@pr-351 --globalEach push to this PR publishes a new version under the Option B — Install from GitHubopencode plugin "github:ranxianglei/opencode-acp#2026-08-29_min-nudge-providers-models" --globalOption C — Download artifact
tar xzf opencode-acp-pr351.tgz
cp -r package/dist ~/.cache/opencode/packages/opencode-acp@latest/node_modules/opencode-acp/dist
This comment is automatically updated on each push. |
…ted .gitignore change Post-PR review of #351: - §5.7.1 required at least one test per PR using preserveRecentMessages > 0; all 17 original tests ran on the preserveRecentMessages: 0 default, which disables the protected-zone / nothingToCompress path (the #207 bug scenario). New 5-turn test: per-model floor suppression → fire → compress → new baseline → fire again, plus a fully-protected turn locking the #207 baseline-reset regression. Mutation-verified (reintroducing the #207 bug fails this test). - Reverted the node_modules/ → node_modules .gitignore edit bundled into e14193c (unsubstantiated 'symlink fix'; no tracked symlink exists). 1054/1054 green (1036 baseline + 18 new). Typecheck + build pass.
[bot] ## Review complete — PR #351 ✅ (2 findings fixed, branch updated to Claim verification (all confirmed against the code)
Design checks all pass: cascade model > provider > global field-by-field ( Findings fixed (commit
|
follow-up) Every tunable compress field now supports the per-provider/per-model override cascade (model > provider > global, field-by-field), not just minNudgeContextPercent. - CompressOverridableConfig = CompressConfig minus permission, the deprecated minContextLimit family, flat model*Limits maps, providers - resolveCompressOverrides / applyCompressOverrides (identity when nothing applies; maxContextLimit excluded from blanket apply) - inject + compress tool entry points swap in the effective config right after model info is known - resolveContextTokenLimit: nested maxContextLimit beats the flat modelMaxLimits map, which beats the global value - validation: per-field type table for all overridable fields - dcp.schema.json: full nested property list at both levels - tests 1061/1061 (+8), mutations M1-M5 verified exact
…evert) Kept both sides: 2 all-field cascade tests + reviewer's production-config growth-cycle test. 1062/1062 tests, typecheck clean.
|
ework再review下看是否会引入回归问题 |
[bot] ## 回归专项复查(f081b28..6576fcf 逐行 diff 追踪) 结论:对现有用户(配置中无 1. 无 providers 时新旧代码逐点等价
2. 验证/告警面
3. 仅有的两处边界差异(都只影响非法配置,且方向更合理)
4. 测试证据
总评:可安全合并(仍需先合 #343;按 §5.1.1.2 合并操作由人工执行)。 |
…overrides); extend changelog
…ft, no behavior change) minContextLimit (default 80%) is the lower bound for turn/iteration reminder nudges. The growth-nudge floor (minNudgeContextPercent + nudgeGrowthTokens, ranxianglei#343/ranxianglei#351) is the maintained mechanism — two parallel 'min' knobs confuse users. Soft deprecation: JSDoc @deprecated, schema [DEPRECATED] prefixes, EN/zh Status DEPRECATED + removal-consequence note, legend reworded (may still take effect until removed). zh default fixed 45% -> 80%. No behavior change; 1053/1053 unchanged.
Model: Claude Sonnet 4.6
Summary
Every tunable
compressfield now supports a three-level cascade override — model > provider > global, field-by-field — via the nestedcompress.providersmap (issue #344 follow-up; scope extended per maintainer request: "应该所有字段都需要三级别 而不是仅仅一个字段").{ "compress": { "nudgeGrowthTokens": 20000, // global default "providers": { "anthropic": { // provider-level "nudgeGrowthTokens": 5000, "models": { "claude-sonnet-4-6": { // model-level (wins per field) "minNudgeContextPercent": 30, "maxContextLimit": "20%" } } } } } }Overridable fields (23)
maxContextLimit,emergencyThresholdPercent,nudgeFrequency,iterationNudgeThreshold,toolOutputNudgeThreshold,nudgeGrowthTokens,minNudgeGrowthRatio,minNudgeGrowthFloor,minNudgeContextPercent,nudgeForce,protectedTools,showCompression,summaryBuffer,protectTags,protectUserMessages,maxSummaryLengthHard,minCompressRange,maxVisibleSegments,keepEmbedMaxChars,lastSegmentSoftBlock,preserveRecentMessages,preserveRecentTokens,preserveLastUserMessage.Not overridable:
permission(tool registration precedes model info), deprecatedminContextLimit/modelMinLimitsfamily (see #352), flatmodelMaxLimits(legacy map, still honored),providersitself.Semantics
maxContextLimitprecedence chain: nested override > flatmodelMaxLimits> global (enforced explicitly inresolveContextTokenLimit).providersmaps deep-merge across the three config file layers per provider/model key (unlike flat maps' replace-inherit).applyCompressOverrides()builds the effective config once per turn (identity return, zero allocation, when nothing applies); injected at the two points where model info is known — nudge injection (inject.ts) and the compress tool (range.ts, afterprepareSession).Validation
OVERRIDE_FIELD_TYPES) covering every overridable field; unknown fields rejected at both levels;dcp.schema.jsonnested schema withadditionalProperties: falseat both levels.Tests — 1062/1062 (typecheck ✓, build ✓)
maxContextLimitexcluded from blanket apply, nested-max beats flat map.maxContextLimitlowers the over-max band; provider-levelnudgeGrowthTokenstightens the growth threshold; model-level 0 disables the floor.6576fcf(§5.7.1 production-config growth-cycle test withpreserveRecentMessages > 0+ PR fix: preserve growth baseline when nothingToCompress #207 regression lock), merged conflict-free-keep-both.Docs
CONFIGURATION.md / CONFIGURATION.zh-CN.md: rewritten
compress.providerssection + new recipe "Per-model tuning of any compress field". Supersedes #345 (closed).Stack
Includes #343 (merged). Merge this before #352 (deprecation annotations reference the same family).