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:
parent
27988801be
commit
2082b7d112
10 changed files with 364 additions and 45 deletions
|
|
@ -14,6 +14,7 @@ import { envelope } from './lib/output.mjs';
|
|||
import { discoverConfigFiles, discoverConfigFilesMulti, discoverFullMachinePaths } from './lib/file-discovery.mjs';
|
||||
import { loadSuppressions, applySuppressions, formatSuppressionSummary } from './lib/suppression.mjs';
|
||||
import { humanizeEnvelope } from './lib/humanizer.mjs';
|
||||
import { resolveContextWindow } from './lib/context-window.mjs';
|
||||
|
||||
// Scanner registry — import order determines execution order
|
||||
import { scan as scanClaudeMd } from './claude-md-linter.mjs';
|
||||
|
|
@ -94,6 +95,11 @@ export async function runAllScanners(targetPath, opts = {}) {
|
|||
// and CNF duplicate-hook findings with config that loads on zero turns. (B3)
|
||||
const excludeCache = opts.excludeCache !== false;
|
||||
|
||||
// B8 — resolve the context window once and thread it to budget-aware scanners
|
||||
// (SKL, CML). Undefined opts.contextWindow → conservative 200k anchor, which is
|
||||
// byte-identical to the pre-B8 default; other scanners ignore the third arg.
|
||||
const contextWindow = resolveContextWindow(opts.contextWindow);
|
||||
|
||||
// Shared file discovery — scanners reuse this
|
||||
let discovery;
|
||||
if (opts.fullMachine) {
|
||||
|
|
@ -112,7 +118,7 @@ export async function runAllScanners(targetPath, opts = {}) {
|
|||
resetCounter();
|
||||
const scanStart = Date.now();
|
||||
try {
|
||||
const result = await scanner.fn(resolvedPath, discovery);
|
||||
const result = await scanner.fn(resolvedPath, discovery, { contextWindow });
|
||||
results.push(result);
|
||||
const count = result.findings.length;
|
||||
const label = opts.humanizedProgress
|
||||
|
|
@ -206,10 +212,13 @@ async function main() {
|
|||
let outputFile = null;
|
||||
let saveBaseline = false;
|
||||
let baselinePath = null;
|
||||
let contextWindow = null;
|
||||
|
||||
for (let i = 0; i < args.length; i++) {
|
||||
if (args[i] === '--output-file' && args[i + 1]) {
|
||||
outputFile = args[++i];
|
||||
} else if (args[i] === '--context-window' && args[i + 1]) {
|
||||
contextWindow = args[++i];
|
||||
} else if (args[i] === '--save-baseline') {
|
||||
saveBaseline = true;
|
||||
} else if (args[i] === '--baseline' && args[i + 1]) {
|
||||
|
|
@ -254,6 +263,7 @@ async function main() {
|
|||
filterFixtures,
|
||||
excludeCache,
|
||||
humanizedProgress,
|
||||
contextWindow,
|
||||
});
|
||||
|
||||
// Default mode runs the humanizer; --json and --raw bypass for v5.0.0 byte-equal output.
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue