feat(skl,cml): --context-window calibration, advisory when unknown (v5.11 B8) [skip-docs]

SKL-002 (skill-listing budget) and CML char-budget now calibrate to a
resolved context window instead of always anchoring at 200k:

- resolveContextWindow(): --context-window <n> calibrates; 'auto' keeps the
  conservative 200k anchor but marks advisory (model→window probing deferred
  to B8b); no flag → 200k anchor, byte-identical to pre-B8 default.
- scaleForWindow(): linear off the 200k anchor (identity at the anchor).
- SKL + CML each keep an untouched default branch (window===200k && !advisory)
  for byte-stability and a calibrated branch; advisory downgrades the budget
  finding from a breach (low/medium) to info.
- Flag wired through scan-orchestrator + posture; runAllScanners resolves once
  and threads { contextWindow } to scanners (others ignore the 3rd arg).
- CPS intentionally excluded: it has no window-anchored budget (fixed
  150-line volatility heuristic), so there is nothing to calibrate.

15 new tests; e2e CLI verified (1M suppresses SKL-002, auto → info, default
unchanged); full suite 1279 green; snapshots byte-stable.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
Kjell Tore Guttormsen 2026-06-23 21:44:52 +02:00
commit 2082b7d112
10 changed files with 364 additions and 45 deletions

View file

@ -102,6 +102,20 @@ describe('assessSkillListingBudget — aggregate math', () => {
assert.equal(r.aggregateChars, 100);
assert.equal(r.scanned, 3);
});
it('accepts a calibrated budget (B8): 17×1000 chars is within a 20,000-tok 1M budget', () => {
const r = assessSkillListingBudget(Array(17).fill(1000), 20_000);
assert.equal(r.aggregateTokens, 4250);
assert.equal(r.budgetTokens, 20_000, 'reports the calibrated budget, not the 200k default');
assert.equal(r.overBudget, false, 'within the relaxed 1M budget');
assert.equal(r.overBy, 0);
});
it('the budget argument defaults to the 200k anchor (byte-stable for existing callers)', () => {
const r = assessSkillListingBudget(Array(17).fill(1000));
assert.equal(r.budgetTokens, AGGREGATE_BUDGET_TOKENS);
assert.equal(r.overBudget, true);
});
});
describe('envFlag', () => {