From 044474715f6970b859e05bc602f3bc61ada39289 Mon Sep 17 00:00:00 2001 From: Claude Date: Tue, 18 Aug 2026 05:53:50 +0000 Subject: [PATCH] plain arm: callee-name residual detail + the drill's proposer stage Two pieces of increment 2, both scoped to what could actually be measured this round. 1. non_literal_assign's detail now carries the callee name for a resolvable call (`field(...)` -> "call:field"), falling back to the generic "call" tag when the callee is itself unresolvable (a call, lambda, subscript - never guessed). Measured payoff on dismech: the 451-row "call" bucket from increment 1 decomposes losslessly into three named LinkML schema factories - call:PermissibleValue 355, call:EnumDefinition 91, call:ConfigDict 5 (355+91+5=451, exact) - which is exactly the kind of drill target increment 1's own histogram predicted without being able to name. 2. New drill.rs: the proposer stage. propose() groups a residual ledger by (reason, detail) for the four reasons dense enough to carry one, and thresholds on support. classify_across_corpora() runs propose() over 2+ labeled corpora and splits candidates into Generic (cleared the bar in EVERY supplied corpus) vs CorpusScoped (some but not all) - an exact, falsifiable rule, not a similarity heuristic. Scope boundary, stated in the module doc and here: this builds grouping + classification ONLY. It does not build a promotion gate that re-runs extraction with a candidate row "active" and checks the coverage delta - that needs a config-consuming extractor (a trie the plain arm reads before deciding a site's classification) that does not exist yet. Claiming this stage ratifies rows would overstate what it measures. Measured, min_support=2, three real corpora (dismech / A2UI SDK / ruff/scripts): 18 candidate rows, ZERO generic. This falsifies my own prediction from the increment-1 handoff that some row would prove cross-corpus generic - worth recording plainly rather than quietly dropping. unresolved_annotation/binop comes closest (96 in dismech, 12 in ruff/scripts) but is silent on the A2UI corpus, so by the exact "fires in ALL supplied corpora" rule it stays CorpusScoped. That is the strict rule working as designed, not a bug: three modest corpora is a small n for "generic," and the alternative (a fuzzy magnitude-similarity threshold) would have manufactured a Generic verdict this data does not support. plain_propose new example: runs classify_across_corpora over N labeled corpora and prints candidates as TOML - data, not Rust, per the earlier design note that the proposer must never write code. Falsifier discipline: every new grouping/threshold/classification site verified by a disable run (mutation asserted present, test observed red, restored, full suite green) - the min_support filter, the Generic/ CorpusScoped split, and the callee-name fallback. Co-Authored-By: Claude Sonnet 5 Claude-Session: https://claude.ai/code/session_01AGVLyRZNEKKBSfBDJfbY3V --- .../ruff_python_spo/examples/plain_propose.rs | 73 +++++ crates/ruff_python_spo/src/drill.rs | 273 ++++++++++++++++++ crates/ruff_python_spo/src/lib.rs | 2 + crates/ruff_python_spo/src/plain.rs | 45 ++- 4 files changed, 392 insertions(+), 1 deletion(-) create mode 100644 crates/ruff_python_spo/examples/plain_propose.rs create mode 100644 crates/ruff_python_spo/src/drill.rs diff --git a/crates/ruff_python_spo/examples/plain_propose.rs b/crates/ruff_python_spo/examples/plain_propose.rs new file mode 100644 index 00000000000000..e66376f149a13c --- /dev/null +++ b/crates/ruff_python_spo/examples/plain_propose.rs @@ -0,0 +1,73 @@ +//! Dev probe: run the plain-Python residual proposer over 2+ corpora and +//! print candidate config rows as TOML — data, not Rust — with each row's +//! `scope` (`generic` iff it cleared `min_support` in EVERY supplied +//! corpus, `corpus_scoped` otherwise). See `drill.rs`'s module doc for +//! exactly what this claims (grouping + classification) and what it does +//! NOT claim (a promotion gate — that needs a config-consuming +//! extractor this probe does not build). +//! +//! Usage: `cargo run -p ruff_python_spo --example plain_propose -- \ +//! = [= ...]` +//! (2+ corpora required — see `classify_across_corpora`'s doc comment.) + +#![expect( + clippy::print_stdout, + reason = "a dev probe's stdout report IS its deliverable" +)] + +use std::path::Path; + +use ruff_python_spo::{RowScope, classify_across_corpora, extract_plain_with_residuals}; + +fn main() { + let mut args = std::env::args().skip(1); + let min_support: usize = args + .next() + .expect("usage: plain_propose