fix(nudge): gate count-triggered tier nudges on the min usage band - #239
fix(nudge): gate count-triggered tier nudges on the min usage band#239ranxianglei wants to merge 1 commit into
Conversation
The growthReady branch's COUNT paths (t2Count >= tier2Trigger / t3Count >= tier3Trigger) fired at ANY usage once the block count was reached — in production a T2-distill nudge injected at 43% usage with only 5872 pending tokens (#237), burning a model turn + cache for negligible reclaim. Gate the count paths on usage >= nudge.minContextLimitPct, the same band the first-sight mass bypass (#194) already uses. The token-mass paths stay ungated: they already require a real 1.5x mass. The idle reason now labels count-ready-but-gated tiers as 'T2 N blocks (count, usage-gated)' so acp_status explains why nothing injected. Fixes #237
04d7101 to
44c96a4
Compare
📦 Built Package ArtifactBranch: Option A — Install from npm PR tag (recommended)In your adapter project: npm install acp-kernel@pr-239Each push to this PR publishes a new version under the Option B — Download artifact
tar xzf acp-kernel-pr239.tgz
npm install ./packageThis comment is automatically updated on each push. |
[bot] 🏷 Duplicate screening verdict: the fix in this PR is already on master via the merged #238 — but the PR carries 4 regression tests that are a genuine, mergeable delta. Labeled Triage (what I verified)1. The bug (#237) is real and the fix is already shipped. The count path ( 2. Layer check — the fix hits the root cause, not the symptom. The "injection storm" (consumed-span rejections, below-min ranges) was a symptom amplified by a repetition-prone model flailing against the tier3-rewrite guard; the root cause is the count path firing below the usage band. Gating the count path on 3. So this PR's fix is redundant with #238. I proved it by rebasing the PR head onto current master: after resolving the 3 conflicts, 4. The real value here is 4 regression tests (2 beyond #238's two):
What I did
RecommendationThe fix needs nothing — it's already live from #238. For the test coverage, my recommendation is yes, keep it: the boundary (Merges are human-only, so I'll leave the merge to you either way.) |
Problem (#237)
The
growthReadybranch indecideNudge(src/compress.ts) has two paths for T2/T3 override:t2Pen >= 1.5x nudgeGrowthTokens(requires a real mass)t2Count >= tier2Trigger/t3Count >= tier3Trigger(fires at ANY usage)In production (session 01a07b3c, 2026-09-09 14:41:34), a T2-distill nudge injected at 43% usage with only 5872 pending tokens — right after a successful T2→T3 distillation had just brought usage down. The count path has no usage gate, so any session that accumulates
tier2TriggerT1 blocks gets a distill nudge on tiny summary mass, burning a model turn + cache for negligible reclaim. With a repetition-prone model this amplified into the perceived injection storm (consumed-span rejections, below-min ranges).Fix
usage >= config.nudge.minContextLimitPct— the same band the first-sight mass bypass (growth floor 挡住新会话/重启继承的大量就绪 pendingT1 —— 首次 nudge 被无谓推迟 #194) already uses. Below the band there is no pressure, so distillation is not needed yet; blocks will distill once usage enters the band (or immediately via the pressure band at ≥75%).reasonnow labels count-ready-but-gated tiers asT2 N blocks (count, usage-gated)soacp_statusexplains why nothing injected.(1.5x), not>= tier2Trigger(previously mislabeled as count even when only the mass path could fire).Tests (4 new, tests/nudge.test.ts)
(count, usage-gated)>=semanticsExisting count-trigger tests (60% usage) are unaffected. Full suite: 584 pass, 0 fail. Typecheck + build clean.