From 829559bbc3bb5be4234f0279fadbae23b6459ae1 Mon Sep 17 00:00:00 2001 From: Weksa Date: Tue, 10 Nov 2026 18:03:26 +0000 Subject: [PATCH 1/3] Pin every DraftClass code string; feed Other real serde errors Co-Authored-By: Claude Sonnet 5.5 --- crates/weksa/src/encode.rs | 30 ++++++++++++++++++------------ 1 file changed, 18 insertions(+), 12 deletions(-) diff --git a/crates/weksa/src/encode.rs b/crates/weksa/src/encode.rs index 3b9f437..c9b245e 100644 --- a/crates/weksa/src/encode.rs +++ b/crates/weksa/src/encode.rs @@ -342,19 +342,25 @@ mod tests { // None keeps a token of the answer. let secret = r#"{"confidence": "canary-token"}"#; assert!(!format!("{:?}", PacketDraft::parse(secret).unwrap_err()).contains("canary")); - // Every code is distinct and none can hold answer text: they are fixed. - let all = [ - DraftClass::Syntax, - DraftClass::Eof, - DraftClass::MissingField, - DraftClass::UnknownField, - DraftClass::InvalidType, - DraftClass::InvalidValue, - DraftClass::BadForm, - DraftClass::Other, + // Other is fed by real serde errors the reader names no class for. + let dup = r#"{"confidence": 1, "confidence": 1}"#; + assert_eq!(class(dup), DraftClass::Other); + let variant = r#"{"stance": {"features": [{"feature": "feature:9", "value": {"Bogus": 1}}]}}"#; + assert_eq!(class(variant), DraftClass::Other); + // Each code is its literal, exactly: a round record keeps these strings. + let pinned = [ + (DraftClass::Syntax, "syntax"), + (DraftClass::Eof, "eof"), + (DraftClass::MissingField, "missing-field"), + (DraftClass::UnknownField, "unknown-field"), + (DraftClass::InvalidType, "invalid-type"), + (DraftClass::InvalidValue, "invalid-value"), + (DraftClass::BadForm, "bad-form"), + (DraftClass::Other, "other"), ]; - let codes: std::collections::BTreeSet<_> = all.iter().map(|c| c.code()).collect(); - assert_eq!(codes.len(), all.len()); + for (c, lit) in pinned { + assert_eq!(c.code(), lit); + } } #[test] -- 2.47.3 From 76984ecd716d0488ea464810a22a38c73ea7fe39 Mon Sep 17 00:00:00 2001 From: Weksa Date: Tue, 10 Nov 2026 18:03:47 +0000 Subject: [PATCH 2/3] show-working: refusal carries the closed reason only, no Debug of LowerError Co-Authored-By: Claude Sonnet 5.5 --- crates/weksa/examples/show_working.rs | 21 ++++++++++++++++++++- tools/show-working/build.py | 2 +- 2 files changed, 21 insertions(+), 2 deletions(-) diff --git a/crates/weksa/examples/show_working.rs b/crates/weksa/examples/show_working.rs index 897bc13..dae9ca9 100644 --- a/crates/weksa/examples/show_working.rs +++ b/crates/weksa/examples/show_working.rs @@ -260,7 +260,7 @@ fn lower_error(e: &LowerError) -> Value { LowerError::AskerNotAddressee { predication } => ("AskerNotAddressee", format!("the agent of {predication} is not the addressee")), LowerError::EntityRead { entity, why } => ("EntityRead", format!("reading {entity}: {why}")), }; - json!({ "variant": variant, "reason": reason, "debug": format!("{e:?}") }) + json!({ "variant": variant, "reason": reason }) } struct World { @@ -375,3 +375,22 @@ fn main() { let _ = std::fs::remove_dir_all(&dir); println!("{}", serde_json::to_string_pretty(&json!({ "fixtures": out })).expect("json")); } + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn refusal_carries_the_closed_reason_never_a_debug_of_the_error() { + // UnknownPhone holds the ruled IPA text; no field of the output may. + let e = LowerError::UnknownPhone { + entity: EntityId::parse("entity:nibu").expect("entity id"), + at: 3, + text: "CANARY-IPA".into(), + }; + let v = lower_error(&e); + assert!(!v.to_string().contains("CANARY")); + let keys: Vec<_> = v.as_object().expect("object").keys().cloned().collect(); + assert_eq!(keys, ["reason", "variant"]); + } +} diff --git a/tools/show-working/build.py b/tools/show-working/build.py index a4c9bc5..bc863f4 100755 --- a/tools/show-working/build.py +++ b/tools/show-working/build.py @@ -182,7 +182,7 @@ def outcome_html(fx): f"

Closeness to the corpus ({e(data['build']['embedder'])})

{''.join(sc)}") if o["kind"] == "lower_error": er = o["error"] - return f"

Refused by the lowerer

LowerError::{e(er['variant'])}: {e(er['reason'])}
{e(er['debug'])}

No text, so no score: nothing was invented to score.

" + return f"

Refused by the lowerer

LowerError::{e(er['variant'])}: {e(er['reason'])}

No text, so no score: nothing was invented to score.

" items = "".join(f"
  • {e(r['rule'])} {e(r['detail'])}
  • " for r in o["refusals"]) return f"

    Refused at admission

      {items}

    The packet never became a lowering input, so the lowerer never saw it.

    " -- 2.47.3 From d4c72c54aaddbdde35f9368ab6cb596258135db6 Mon Sep 17 00:00:00 2001 From: Weksa Date: Tue, 10 Nov 2026 18:04:40 +0000 Subject: [PATCH 3/3] show-working guard: reject Validation/Test items by split before writing Co-Authored-By: Claude Sonnet 5.5 --- tools/show-working/build.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/tools/show-working/build.py b/tools/show-working/build.py index bc863f4..84e5189 100755 --- a/tools/show-working/build.py +++ b/tools/show-working/build.py @@ -49,7 +49,8 @@ data = json.loads(run([str(target / "examples" / "show_working")])) REFS_EXTRA = ["item:nibu:2"] # The page publishes only the corpus items its fixtures cite (plus the extra # reference each lowered fixture is scored against). A Validation or Test item -# is never published: the dump holds every split. +# is never published, even when hand-added to REFS_EXTRA or cited by a fixture: +# the dump holds every split, and the guard below reads each item's split. cited = {fx["corpus_item"] for fx in data["fixtures"]} | set(REFS_EXTRA) corpus = {} for m in re.finditer(r'^(item:\S+) \[(.*?)\]\n "(.*)"$', run([str(weksa_bin), "corpus", "dump"]), re.M): @@ -57,6 +58,10 @@ for m in re.finditer(r'^(item:\S+) \[(.*?)\]\n "(.*)"$', run([str(weksa_bin), " corpus[m.group(1)] = {"text": m.group(3), "meta": m.group(2)} if set(corpus) != cited: sys.exit(f"corpus dump lacks cited items {sorted(cited - set(corpus))}; unexpected {sorted(set(corpus) - cited)}") +# The dump's meta ends with the split (`... group=g Validation`, `... excluded`). +held_out = {k: v["meta"].split()[-1] for k, v in corpus.items() if v["meta"].split()[-1] in ("Validation", "Test")} +if held_out: + sys.exit(f"refusing to publish held-out items (nothing written): {held_out}") model = "qwen3-embedding:0.6b" @@ -84,11 +89,6 @@ data["build"] = { } out_dir.mkdir(parents=True, exist_ok=True) (out_dir / "data.json").write_text(json.dumps(data, indent=1, ensure_ascii=False), encoding="utf-8") -# Guard, read back from the published file: no item key outside what a fixture cites. -published = json.loads((out_dir / "data.json").read_text(encoding="utf-8")) -stray = set(published["corpus"]) - cited -if stray or set(published["corpus"]) != cited: - sys.exit(f"data.json corpus {sorted(published['corpus'])} is not exactly the cited items {sorted(cited)}; stray: {sorted(stray)}") # -- 3. the page -------------------------------------------------------------- CSS = """ -- 2.47.3