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>
77 lines
3.4 KiB
JavaScript
77 lines
3.4 KiB
JavaScript
/**
|
|
* Context-window constants — single source of truth.
|
|
*
|
|
* Several Claude Code budgets scale with the model's context window:
|
|
* - the skill listing is allotted ~2% of context (CC 2.1.32, changelog L2860);
|
|
* - the "CLAUDE.md is too long" warning threshold scales with it (CC 2.1.169).
|
|
*
|
|
* We cannot observe the user's actual context window, so scanners anchor on a
|
|
* conservative 200k window (the smallest common size — it fires earliest, the
|
|
* safe default when the window is unknown) and disclose the relaxed 1M figure.
|
|
*
|
|
* Zero external dependencies.
|
|
*/
|
|
|
|
// Conservative anchor: the smallest common context window. Budgets anchored
|
|
// here fire earliest, which is the safe default when the window is unknown.
|
|
export const CONTEXT_WINDOW_ANCHOR = 200_000;
|
|
|
|
// Large context window (Opus/Sonnet 1M tier). Used to disclose how a budget
|
|
// relaxes on large-context models.
|
|
export const LARGE_CONTEXT_WINDOW = 1_000_000;
|
|
|
|
// How much larger the 1M window is than the 200k anchor (= 5). A budget that
|
|
// scales linearly with the window relaxes by this factor at 1M.
|
|
export const LARGE_CONTEXT_SCALE = LARGE_CONTEXT_WINDOW / CONTEXT_WINDOW_ANCHOR;
|
|
|
|
// Dependency-free thousands separator (repo invariant: zero external deps).
|
|
export const withCommas = (n) => String(n).replace(/\B(?=(\d{3})+(?!\d))/g, ',');
|
|
|
|
/**
|
|
* @typedef {object} ResolvedContextWindow
|
|
* @property {number} window - the context window budgets calibrate against
|
|
* @property {boolean} advisory - true when the window is unknown: keep the anchor
|
|
* but downgrade budget findings to info instead of
|
|
* firing them as a breach
|
|
* @property {'default'|'explicit'|'auto-unresolved'} source
|
|
*/
|
|
|
|
/**
|
|
* Resolve the raw `--context-window` CLI value into a window + advisory flag.
|
|
*
|
|
* Design (B8): the DEFAULT (no flag) is byte-identical to the pre-B8 behavior —
|
|
* the conservative 200k anchor at full severity. Only an explicit value changes
|
|
* calibration. `auto` asks the tool to figure out the window; until model→window
|
|
* probing ships (B8b) it cannot, so it keeps the conservative anchor but marks the
|
|
* result advisory so SKL/CML downgrade their budget findings to info rather than
|
|
* "crying wolf" with a breach on a window we cannot confirm.
|
|
*
|
|
* @param {string|number|null|undefined} arg
|
|
* @returns {ResolvedContextWindow}
|
|
*/
|
|
export function resolveContextWindow(arg) {
|
|
if (arg == null) {
|
|
return { window: CONTEXT_WINDOW_ANCHOR, advisory: false, source: 'default' };
|
|
}
|
|
if (String(arg).trim().toLowerCase() === 'auto') {
|
|
return { window: CONTEXT_WINDOW_ANCHOR, advisory: true, source: 'auto-unresolved' };
|
|
}
|
|
const n = typeof arg === 'number' ? arg : parseInt(String(arg).trim(), 10);
|
|
if (Number.isFinite(n) && n > 0) {
|
|
return { window: n, advisory: false, source: 'explicit' };
|
|
}
|
|
// Unparseable / non-positive: fall back to the conservative default (no advisory).
|
|
return { window: CONTEXT_WINDOW_ANCHOR, advisory: false, source: 'default' };
|
|
}
|
|
|
|
/**
|
|
* Scale a 200k-anchored budget to a given context window. Linear in the window,
|
|
* so it is the identity at the anchor (keeps the default byte-stable).
|
|
*
|
|
* @param {number} anchorValue - the budget/threshold defined at the 200k anchor
|
|
* @param {number} window - the target context window
|
|
* @returns {number}
|
|
*/
|
|
export function scaleForWindow(anchorValue, window) {
|
|
return Math.round(anchorValue * (window / CONTEXT_WINDOW_ANCHOR));
|
|
}
|