C1: two entries the optimization lens can cite for the model/effort axis, both read out of the primary sources in this session rather than from the plan's 2026-07-14 summary of them. Verifying corrected the plan's own numbers: effort is settable in SIX places, not five (/effort, the /model slider, --effort, CLAUDE_CODE_EFFORT_LEVEL, settings effortLevel, skill/subagent frontmatter), the default is high on every supporting model EXCEPT Opus 4.7 (xhigh), and the level count is model-dependent (Opus 4.6 and Sonnet 4.6 have no xhigh). - BP-MODEL-001: subagent `model` defaults to `inherit`, so a subagent that names no model costs what the session costs; the documented pin is overridable by CLAUDE_CODE_SUBAGENT_MODEL and per-invocation model - BP-MODEL-002: effort is an axis separate from model choice, and higher is not universally better (`max` "may show diminishing returns and is prone to overthinking") - both carry the 2026-07-07 model/effort blog as a corroborating source with a real `published` date, so B1's evidence-age rule has teeth: measured stale with reasons ['evidence-age'] at a reference date 378 days past publication while their verified stamps are pristine. The docs pages themselves get NO published date — they carry none, and guessing one in the field whose whole job is dating evidence is the lie the rule exists to catch - new blanket guard: every corroborating source must carry a parseable published date. Without it newestEvidenceMs() returns null and the entry stays green on evidence of any age — a silent hole. Seen red against its own defect before it was trusted - knowledge-refresh-cli's stale branch no longer expires on every new entry: its reference date has to sit after every `verified` stamp, was bumped once for BP-SUB-001 and would have needed a third bump now, so it moves to a date no stamp can reach Register 14 -> 16 entries. Frozen v5.0.0 snapshots untouched (no scanner output changes); suite 1579/0. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01NKjojcdYYiCQP5AudUyQ5e
117 lines
5.1 KiB
JavaScript
117 lines
5.1 KiB
JavaScript
import { describe, it } from 'node:test';
|
|
import assert from 'node:assert/strict';
|
|
import { resolve, join } from 'node:path';
|
|
import { fileURLToPath } from 'node:url';
|
|
import { execFileSync } from 'node:child_process';
|
|
import { readFileSync, mkdtempSync } from 'node:fs';
|
|
import { tmpdir } from 'node:os';
|
|
|
|
const __dirname = fileURLToPath(new URL('.', import.meta.url));
|
|
const CLI = resolve(__dirname, '../../scanners/knowledge-refresh-cli.mjs');
|
|
|
|
// A fixed reference date keeps both branches reachable regardless of the real clock.
|
|
//
|
|
// REF is used by the fresh branch only, and needs no maintenance: an entry verified
|
|
// after it has a negative age and still counts as fresh.
|
|
const REF = '2026-08-01';
|
|
|
|
// The stale branch needs a date strictly after EVERY entry's `verified` stamp — with
|
|
// `--stale-after 0` an entry verified later than the reference date has a negative age,
|
|
// lands in the fresh bucket, and empties nothing.
|
|
//
|
|
// This was REF itself, bumped 2026-06-21 → 2026-08-01 when BP-SUB-001 landed with a
|
|
// genuine 2026-07-31 stamp, and it expired a second time when BP-MODEL-001/002 landed
|
|
// verified 2026-08-10. Backdating an entry to fit the test would lie about when its
|
|
// source was read, and re-deriving the date from the register would just assert the
|
|
// register against itself. A date no verification stamp can reach satisfies the
|
|
// requirement by construction, so no future entry expires it.
|
|
const REF_AFTER_EVERY_STAMP = '2999-01-01';
|
|
|
|
function runCli(extraArgs) {
|
|
try {
|
|
const stdout = execFileSync('node', [CLI, ...extraArgs], { encoding: 'utf-8', timeout: 15000 });
|
|
return { status: 0, stdout };
|
|
} catch (err) {
|
|
return { status: err.status, stdout: err.stdout || '', stderr: err.stderr || '' };
|
|
}
|
|
}
|
|
|
|
describe('knowledge-refresh-cli — exit codes', () => {
|
|
it('exits 0 when every entry is fresh', () => {
|
|
const { status, stdout } = runCli(['--reference-date', REF, '--stale-after', '90']);
|
|
assert.equal(status, 0);
|
|
const out = JSON.parse(stdout);
|
|
assert.equal(out.counts.stale, 0);
|
|
});
|
|
|
|
it('exits 1 (advisory) when one or more entries are stale', () => {
|
|
// stale-after 0 → anything verified before the reference date is stale.
|
|
const { status, stdout } = runCli([
|
|
'--reference-date', REF_AFTER_EVERY_STAMP,
|
|
'--stale-after', '0',
|
|
]);
|
|
assert.equal(status, 1);
|
|
const out = JSON.parse(stdout);
|
|
assert.ok(out.counts.stale > 0);
|
|
assert.equal(out.counts.fresh, 0);
|
|
});
|
|
|
|
it('exits 3 on an invalid --stale-after', () => {
|
|
assert.equal(runCli(['--stale-after', 'abc']).status, 3);
|
|
assert.equal(runCli(['--stale-after', '-5']).status, 3);
|
|
});
|
|
|
|
it('exits 3 on a malformed --reference-date', () => {
|
|
assert.equal(runCli(['--reference-date', '20-06-2026']).status, 3);
|
|
});
|
|
});
|
|
|
|
describe('knowledge-refresh-cli — payload shape', () => {
|
|
it('emits the expected keys + a consistent count triple', () => {
|
|
const { stdout } = runCli(['--reference-date', REF, '--stale-after', '90']);
|
|
const out = JSON.parse(stdout);
|
|
assert.equal(out.status, 'ok');
|
|
assert.match(out.registerPath, /knowledge\/best-practices\.json$/);
|
|
assert.equal(out.referenceDate, REF);
|
|
assert.equal(out.staleAfterDays, 90);
|
|
assert.ok(Array.isArray(out.stale));
|
|
assert.ok(Array.isArray(out.fresh));
|
|
assert.equal(out.counts.total, out.counts.stale + out.counts.fresh);
|
|
assert.ok(out.counts.total > 0, 'bundled register is non-empty');
|
|
});
|
|
|
|
it('is always dryRun:true regardless of the flag, and echoes the request', () => {
|
|
const without = JSON.parse(runCli(['--reference-date', REF]).stdout);
|
|
assert.equal(without.dryRun, true);
|
|
assert.equal(without.requestedDryRun, false);
|
|
const withFlag = JSON.parse(runCli(['--reference-date', REF, '--dry-run']).stdout);
|
|
assert.equal(withFlag.dryRun, true);
|
|
assert.equal(withFlag.requestedDryRun, true);
|
|
});
|
|
|
|
it('stale entries carry provenance (id, verified, ageDays, url, claim)', () => {
|
|
const out = JSON.parse(runCli(['--reference-date', REF, '--stale-after', '0']).stdout);
|
|
const s = out.stale[0];
|
|
assert.match(s.id, /^BP-[A-Z]+-\d{3}$/);
|
|
assert.match(s.verified, /^\d{4}-\d{2}-\d{2}$/);
|
|
// Was `=== 1`, which only held while every entry shared one verified date.
|
|
// Assert the invariant that actually matters: ageDays is a positive integer
|
|
// and agrees with this entry's own `verified` date against the reference.
|
|
const expectedAge = Math.round((Date.parse(REF) - Date.parse(s.verified)) / 86400000);
|
|
assert.equal(s.ageDays, expectedAge);
|
|
assert.ok(s.ageDays >= 1, 'a stale entry is at least a day old');
|
|
assert.ok(s.url && s.claim);
|
|
});
|
|
});
|
|
|
|
describe('knowledge-refresh-cli — --output-file', () => {
|
|
it('writes JSON to the file and not to stdout', () => {
|
|
const dir = mkdtempSync(join(tmpdir(), 'kr-cli-'));
|
|
const out = join(dir, 'refresh.json');
|
|
const { stdout } = runCli(['--reference-date', REF, '--stale-after', '90', '--output-file', out]);
|
|
assert.equal(stdout.trim(), '', 'stdout is silent when --output-file is given');
|
|
const written = JSON.parse(readFileSync(out, 'utf-8'));
|
|
assert.equal(written.status, 'ok');
|
|
assert.equal(written.counts.stale, 0);
|
|
});
|
|
});
|