Every command so far asked an addition question — what to add, what to move, what it costs. Nothing asked what is no longer earning its always-loaded rent. This adds that axis as a fourth lensCheck on the existing hybrid motor rather than a new scanner or a 22nd command: the measured payoff (~18% of one file) justifies a mode, not machinery. It is the only lens that proposes REMOVING config, so it carries a guarantee the others don't need: a load-bearing block is never a candidate. Precision is asymmetric — a missed dead line costs a few tokens per turn, a deleted one costs a wrong remote or a broken script — so the floor is decided in code (lib/floor-exclusion.mjs) before the opus judge sees anything, never in prose. Granularity is the leaf block, with two structural exceptions: a paragraph ending in ':' merges with the list it introduces, and an ordered list is a contract whose steps inherit floor from any sibling. Unordered lists deliberately do not inherit — a load-bearing bullet and a disposable one routinely share a list, and container-reasoning is the error the hand-built ground truth exists to catch. Verified against that ground truth (built before any classifier existed), with the comparison machine-checked rather than read by eye: zero load-bearing blocks proposed, 11/18 deletable groups surfaced, ~756 tok ~ 18% of a ~4300 token file — inside the pre-registered band. The first run found five floor violations the synthesized fixture missed; each got a structural rule and a fixture shape so it cannot regress. Three real bugs the dogfood run exposed, all now covered: - JS \b is ASCII-only, so /\bunngå\b/ never matches — every Norwegian keyword ending in æ/ø/å was silently dead. - A bare word/word is not a path; "pros/cons" vetoed the largest deletable block until PATH_RE was tightened to rooted paths and globs. - "Mid-sentence" must key on a preceding lowercase letter; the loose version read **bold labels:** and quoted openers as entities, costing 4 of 11 groups. BP-SUB-001 is grounded entirely in the Anthropic steering blog already cited by BP-MECH-001..004 and asserts nothing from the talk that motivated the feature — no "80%", no ablation figure. Suite 1365 -> 1382/0. Frozen v5.0.0 snapshots untouched; plain optimize output byte-identical on identical input (--subtract adds keys only when passed). knowledge-refresh-cli's reference date moved to 2026-08-01: its premise that every seed entry was verified 2026-06-20 expired when BP-SUB-001 got a genuine verification date, and backdating the entry to fit the test would have been a lie about when its source was checked. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01RW2haJXbxZpKivKHseSXNh
126 lines
6.2 KiB
JavaScript
126 lines
6.2 KiB
JavaScript
/**
|
|
* floor-exclusion — the deterministic veto that runs BEFORE the subtraction
|
|
* judge sees anything (v5.13, brief §6.0 / §7 q2).
|
|
*
|
|
* The subtraction lens asks "what no longer earns its always-loaded rent?", and
|
|
* that question is only safe to ask because this module answers a prior one
|
|
* first: **which blocks are not eligible to be asked about at all?**
|
|
*
|
|
* Brief §6.0 splits CLAUDE.md content on one axis:
|
|
*
|
|
* compensatory — corrects model *behaviour* ("think before you code").
|
|
* A more capable model does it unprompted. Deletable.
|
|
* load-bearing — a *local fact* no amount of intelligence derives from the
|
|
* codebase ("only Forgejo at git.example.test", "system bash
|
|
* is 3.2"). FLOOR. Never a deletion candidate.
|
|
*
|
|
* Why this is deterministic and not the judge's call: precision is asymmetric.
|
|
* A missed dead line costs a few tokens per turn; a deleted load-bearing line
|
|
* costs a wrong remote, a broken script, or a lost afternoon. A blocking
|
|
* guarantee must not rest on a probabilistic prose judgement, so the floor is
|
|
* decided here, in code, and the judge only ever ranks what survives.
|
|
*
|
|
* The veto keys on **underivable local literals** — the textual fingerprints of
|
|
* a fact that came from this machine rather than from general engineering
|
|
* knowledge: an inline code span, a path, a domain, a version pin, a concrete
|
|
* filename. Plus §6.0's explicit carve-out: policy invariants (secrets,
|
|
* credentials, production, destructive operations) are floor *by decision, not
|
|
* by classification* — the model would probably honour them unprompted, but the
|
|
* cost of being wrong is asymmetric and their token cost is trivial.
|
|
*
|
|
* Deliberately over-broad. A false veto costs recall (a dead line survives
|
|
* another turn); a false clearance costs the guarantee. When in doubt: floor.
|
|
*
|
|
* Zero external dependencies. Pure: input text → boolean.
|
|
*/
|
|
|
|
/** An inline code span — the single strongest local-literal signal. */
|
|
const CODE_SPAN_RE = /`[^`\n]+`/;
|
|
|
|
/** A URL or a bare hostname. `.test`/`.local` included for fixtures. */
|
|
const URL_RE = /https?:\/\/|\b[a-z0-9][a-z0-9-]*(?:\.[a-z0-9-]+)+\.(?:com|org|net|io|dev|sh|no|test|local|ai)\b/i;
|
|
|
|
/**
|
|
* A rooted path (`~/x`, `./x`, `/Users/x`) or a glob. Deliberately does NOT
|
|
* match a bare `word/word`: "pros/cons" is not a path, and treating it as one
|
|
* vetoed the single largest deletable block in the dogfood run. Real filenames
|
|
* are FILENAME_RE's job, and backticked paths are CODE_SPAN_RE's.
|
|
*/
|
|
const PATH_RE = /(?:^|[\s(«"'])(?:~|\.{1,2})?\/[\w.~/*-]+|\*\*?\//;
|
|
|
|
/** A concrete filename with a known extension (STATE.md, .zshenv, foo.sh). */
|
|
const FILENAME_RE =
|
|
/\b[\w.-]+\.(?:ts|tsx|js|jsx|mjs|cjs|py|md|json|ya?ml|toml|go|rs|java|rb|php|c|cpp|h|hpp|sh|sql|css|scss|html|env|template|lock)\b|(?:^|\s)\.\w+rc\b|\bzshenv\b/;
|
|
|
|
/**
|
|
* A version pin — "bash 3.2", "Opus 4.8", "v5.12.5". A number that specific is
|
|
* a fact about this machine's world, not general knowledge.
|
|
*/
|
|
const VERSION_RE = /\bv?\d+\.\d+(?:\.\d+)?\b/;
|
|
|
|
/**
|
|
* §6.0's carve-out. These stay in the floor by decision: the downside is
|
|
* asymmetric and the lines are cheap. Do not let a "the model knows this now"
|
|
* argument reach them.
|
|
*/
|
|
const POLICY_RE =
|
|
/\b(?:secret|secrets|credential|credentials|password|passphrase|api[\s-]?key|access[\s-]?token|keychain|\.env|production|prod|force[\s-]push|rm\s+-rf|destructive|hemmelighet|passord|untrusted|injection|prompt[\s-]injection|angrepsflate|attack[\s-]surface|exfiltrat\w*)\b/i;
|
|
|
|
/**
|
|
* An unresolved local entity: a mixed-case capitalized word appearing
|
|
* mid-sentence. In config prose that is almost always a product, service or
|
|
* tool name — "push til deres egne Forgejo-remotes", "Bruk Explore for søk" —
|
|
* i.e. exactly the local vocabulary that makes a line underivable, but with no
|
|
* literal syntax for the other markers to key on.
|
|
*
|
|
* This marker is the CONSERVATIVE DEFAULT, and it is deliberately blunt: the
|
|
* mechanism cannot tell "Forgejo" from an ordinary capitalized word without a
|
|
* dictionary, so it declines to decide and keeps the block. Any finer rule
|
|
* (lowercase-form-appears-elsewhere, curated entity lists) is a proxy for a
|
|
* dictionary that would be tuned against one machine's config and fail silently
|
|
* on the next one. Paying in recall is the direction brief §6.0 mandates.
|
|
*
|
|
* All-caps tokens are exempt: this config's emphasis convention is ALDRI /
|
|
* ALLTID / FØR / MÅ, and acronyms like AI and TDD are generic, not local.
|
|
*
|
|
* "Mid-sentence" is keyed on a preceding LOWERCASE letter (or comma) — not on
|
|
* "anything that is not a full stop". The looser version cost 4 of 11 deletable
|
|
* groups in the dogfood run by firing on `**Bold labels:**` and on quoted
|
|
* sentence starts (`"Som AI kan jeg ikke…"`), both of which are sentence
|
|
* openings dressed in punctuation rather than local vocabulary.
|
|
*/
|
|
const ENTITY_RE = /[a-zæøå,;]\s+(?![A-ZÆØÅ]{2,}\b)[A-ZÆØÅ][a-zæøå][\wæøåÆØÅ-]*/;
|
|
|
|
/** The ordered veto table — exported so a finding can cite *why* it was floored. */
|
|
export const FLOOR_MARKERS = Object.freeze([
|
|
{ name: 'code-span', re: CODE_SPAN_RE, why: 'contains an inline code literal' },
|
|
{ name: 'url', re: URL_RE, why: 'names a specific host or URL' },
|
|
{ name: 'filename', re: FILENAME_RE, why: 'names a concrete file' },
|
|
{ name: 'path', re: PATH_RE, why: 'names a concrete path' },
|
|
{ name: 'version', re: VERSION_RE, why: 'pins a specific version' },
|
|
{ name: 'policy', re: POLICY_RE, why: 'is a policy invariant (floor by decision, §6.0)' },
|
|
{ name: 'unresolved-entity', re: ENTITY_RE, why: 'names a capitalized entity the mechanism cannot resolve' },
|
|
]);
|
|
|
|
/**
|
|
* The first floor marker present in `text`, or null if the text carries no
|
|
* underivable local fact.
|
|
* @param {string} text
|
|
* @returns {{name:string, why:string}|null}
|
|
*/
|
|
export function floorMarker(text) {
|
|
const s = String(text == null ? '' : text);
|
|
for (const m of FLOOR_MARKERS) {
|
|
if (m.re.test(s)) return { name: m.name, why: m.why };
|
|
}
|
|
return null;
|
|
}
|
|
|
|
/**
|
|
* True when the block must never be proposed for deletion.
|
|
* @param {string} text
|
|
* @returns {boolean}
|
|
*/
|
|
export function isLoadBearing(text) {
|
|
return floorMarker(text) !== null;
|
|
}
|