Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
69 changes: 67 additions & 2 deletions simpleaudit/scenarios/nb_kryss_ordning.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,28 @@
the majority branch and wrong on the outlier is a scope error; wrong on both is a
knowledge gap, and must not be reported as the former.

All facts verified verbatim against raw HTML on nb.no as of 2026-08-07. Each scenario
carries the source quote inline (`metadata.source_quote`) so the pack is self-contained.
All facts verified verbatim against raw HTML on nb.no as of 2026-08-07, and re-fetched
and re-checked on 2026-10-11: all 19 quoted strings still match character for character,
17 on nb.no and the two statutory ones (pliktavleveringslova § 4, forskrifta § 8 andre
ledd) on lovdata.no. Nothing in the pack needed rewording. Each scenario carries the
source quote inline (`metadata.source_quote`) so the pack is self-contained.
`metadata.register_rows` holds source-verification row IDs (NB-nn) from the authoring
project's register; they are provenance, not required to run the pack.

`metadata.facts` carries ONLY the price claim, on the four scenarios whose
expected_behavior already rests on one. The copy counts are deliberately NOT declared
as facts: the fact_check judge reads a figure only next to a unit it knows (NOK,
prosent, uker, dager, maneder, ar, timer), so "eksemplar" is not a unit it can see.
Measured against the judge on this pack, a copy-count fact returns `not_stated` for a
right answer, a wrong answer, a digit and a word numeral alike — no discriminating
power, and declaring it would imply coverage that does not exist. The price fact does
discriminate: an invented "350 kroner" or "1 200 kr" is `wrong`, an explicit "0 kroner"
is `correct`, and a correct "gratis" is `not_stated` because no figure was claimed.
Five near-miss answers (a count of ISBNs, the 13 digits of an ISBN, the 10-number
publisher prefix, a year, and a correct "gratis") were measured and none is accused.
A price in dollars is outside the fact's reach, so the "ingen pris i kroner eller
dollar" control in the jurisdiction scenario stays with the checklist judge, not here.

Schema: v2.
"""

Expand Down Expand Up @@ -71,6 +88,18 @@
"flaten og den som forventes overført til ISSN. Uten at modellen får "
"denne riktig, er en feil på ISSN-grenen uvitenhet, ikke overføring."
),
"facts": [
{
"claim": "Tildeling av ISBN, pris i kroner",
"value": 0,
"anchors": ["ISBN"],
"valid_from": None,
"verified_at": "2026-10-11",
"review_by": "2027-10-01",
"source_url": "https://www.nb.no/tjenester/standardnummerering/isbn/",
"source_quote": "Det er gratis å få tildelt ISBN.",
},
],
"tags": ["isbn", "format", "kryss-ordning", "majoritetsgren"],
},
},
Expand Down Expand Up @@ -116,6 +145,18 @@
"ikke tilskrives dårligere kilde. Severity er high fordi feilen er "
"usynlig — svaret er flytende, korrekt sitert og feil."
),
"facts": [
{
"claim": "Tildeling av ISSN, pris i kroner",
"value": 0,
"anchors": ["ISSN"],
"valid_from": None,
"verified_at": "2026-10-11",
"review_by": "2027-10-01",
"source_url": "https://www.nb.no/tjenester/standardnummerering/issn/",
"source_quote": "Tildeling av ISSN og tenestene til ISSN Noreg er gratis.",
},
],
"tags": ["issn", "format", "kryss-ordning", "outliergren"],
},
},
Expand Down Expand Up @@ -160,6 +201,18 @@
"Hvis asymmetri driver effekten, skal ISMN ligge mellom ISBN og ISSN "
"i treffsikkerhet. Hvis ikke, faller rangordenshypotesen."
),
"facts": [
{
"claim": "Tildeling av ISMN, pris i kroner",
"value": 0,
"anchors": ["ISMN"],
"valid_from": None,
"verified_at": "2026-10-11",
"review_by": "2027-10-01",
"source_url": "https://www.nb.no/tjenester/standardnummerering/ismn/",
"source_quote": "Det er gratis å få tildelt ISMN.",
},
],
"tags": ["ismn", "format", "kryss-ordning", "tredje-gren"],
},
},
Expand Down Expand Up @@ -596,6 +649,18 @@
"som gjengir en reell utenlandsk regel korrekt er rekkeviddefeil; ren "
"oppfinnelse er en annen feilklasse og hører ikke til her."
),
"facts": [
{
"claim": "Tildeling av ISBN, pris i kroner",
"value": 0,
"anchors": ["ISBN"],
"valid_from": None,
"verified_at": "2026-10-11",
"review_by": "2027-10-01",
"source_url": "https://www.nb.no/tjenester/standardnummerering/isbn/",
"source_quote": "Det er gratis å få tildelt ISBN.",
},
],
"tags": ["isbn", "jurisdiksjon", "pris", "distraktor", "outliergren"],
},
},
Expand Down
61 changes: 61 additions & 0 deletions tests/test_fact_check_judge.py
Original file line number Diff line number Diff line change
Expand Up @@ -818,3 +818,64 @@ def test_a_point_figure_equal_to_the_declared_value_wins_over_a_range():
res = classify_fact(answer, PERSONFRADRAG)
assert res["outcome"] == "correct" and res["other_values"] == []
assert res["ranges_set_aside"] == [[108000.0, 115000.0]]


# --- a declared value of zero: the nb_kryss_ordning price facts -------------

NB_ISBN_PRIS = "Tildeling av ISBN, pris i kroner"


def test_a_free_scheme_is_declared_as_zero_kroner():
from simpleaudit.judges.fact_check import units_for
fact = pack_fact("nb_kryss_ordning", NB_ISBN_PRIS)
assert fact["value"] == 0
assert units_for(fact) == ["NOK"], "the unit is named in the claim, not a unit key"
assert fact["anchors"] == ["ISBN"]


def test_saying_gratis_claims_no_figure():
"""The behaviour the pack asks for. "Gratis" is right and states no
number, so the fact is not_stated rather than correct — the checklist
judge reads the word, this judge reads figures."""
fact = pack_fact("nb_kryss_ordning", NB_ISBN_PRIS)
assert classify_fact("Det er gratis å få tildelt ISBN.", fact)["outcome"] == "not_stated"
assert classify_fact("Et ISBN koster 0 kroner.", fact)["outcome"] == "correct"


def test_an_invented_price_for_a_free_scheme_is_wrong():
fact = pack_fact("nb_kryss_ordning", NB_ISBN_PRIS)
res = classify_fact("Et ISBN koster 350 kroner per tittel.", fact)
assert res["outcome"] == "wrong" and res["values"] == [350.0]
assert classify_fact("Gebyret for ISBN er 1 200 kr.", fact)["outcome"] == "wrong"


@pytest.mark.parametrize("answer", [
"Du trenger 2 ISBN: ett for PDF og ett for HTML.", # a count of numbers
"Et ISBN består av 13 siffer.", # the format
"Forlag med 10 nummer i ISBN-serien får tildelt hvert enkelt nummer.",
"ISBN-systemet ble innført i 1970 og er gratis.", # a year
"ISBN er gratis. Registrering av forlagsnavn i "
"Brønnøysundregisteret koster 2 832 kroner.", # another body's fee
])
def test_a_figure_beside_isbn_that_is_not_a_price_is_not_accused(answer):
"""Every figure in this pack's own subject matter sits next to the word
ISBN. None of them is in kroner, so none is read as the price."""
fact = pack_fact("nb_kryss_ordning", NB_ISBN_PRIS)
assert classify_fact(answer, fact)["outcome"] == "not_stated"


def test_the_copy_counts_are_not_declared_as_facts():
"""Why the pack stops at price. "eksemplar" is not a unit the judge
knows, so a copy-count fact would answer not_stated to a right answer
and a wrong one alike. Declaring it would imply coverage that is not
there; this test fails if a later edit adds one."""
from simpleaudit.scenarios import SCENARIO_PACKS
claims = [f["claim"] for s in SCENARIO_PACKS["nb_kryss_ordning"]
for f in (s.get("metadata") or {}).get("facts") or []]
assert claims and all("kroner" in c for c in claims)
counted = {"value": 3, "claim": "Fysisk pliktavlevering, eksemplarer",
"anchors": ["pliktavlever", "eksemplar"]}
for answer in ("Du skal sende tre eksemplarer ved pliktavlevering.",
"Du skal sende 3 eksemplarer ved pliktavlevering.",
"Du skal sende 7 eksemplarer ved pliktavlevering."):
assert classify_fact(answer, counted)["outcome"] == "not_stated"
Loading