What the checks are measured against, beyond the two examples each one ships.
| file | what it is | how it is labelled |
|---|---|---|
gold.json |
22 questions the TypeSafe docs discuss in prose | by the docs, with the quotation and the defect named |
docs-examples.json |
76 entries, 52 distinct, from the docs' cookbooks, patterns and demos | correct by publication: TypeSafe wrote them as how to do it |
field.json |
62 questions from jev-bias-bench and the Atarim API |
unlabelled |
scores.json |
what every check put on every one of them | a run of harvest.py |
build.py |
the three above, as query files jevlint can read | - |
dogfood.py |
the catalogue's own check wordings, as query files | - |
squad.py |
state/answer-absent against SQuAD 2.0, fetched on demand |
by the SQuAD annotators |
decision.py |
state/answer-absent against decision-v7, whose states carry a policy that can be taken away |
by decision-v7 |
advice.py |
whether following choice/no-fallback improves the answer, not just the report |
by decision-v7 |
rewrite.py |
the same for question/double-negative, with the rewriting done blind by a panel |
by decision-v7 |
wording.py |
three wording checks at once, on material the model does not already answer perfectly | by decision-v7 |
families.py |
whether the two date and arithmetic checks fire where decision-v7's answers fail | by decision-v7 |
levels.py |
whether rewriting a Score's levels as situations puts reviews where they belong | by decision-v7 |
contradiction.py |
what inverted criteria cost, against criteria that agree with the instruction | by decision-v7 |
external.py |
query/overlapping-questions against Quora's duplicate questions |
by that dataset |
planted.py |
state/adversarial-content on documents with a line planted in them |
by the injection being known |
unread.py |
state/irrelevant-field on states with a field nothing reads |
by the field being known |
boundary.py |
what a vague boundary costs in how steadily the answer repeats | none needed |
encoded.py |
whether a value written as hex costs an answer | computed, not judged |
drift.py |
how far a re-harvest moved the readings, and which verdicts changed | - |
arms/ |
the samples and the blind rewrites the four arm experiments run over | - |
pages.py |
mirrors the documentation pages the docs tier was lifted from | - |
states.py |
puts back the state each docs example was run against | - |
python3 corpus/build.py # the three source files into the query files in local/corpus
python3 corpus/harvest.py [path/to/.env] # score them, 250 calls
python3 corpus/measure.py # recall on the gold negatives, firing rate on the rest
python3 corpus/drift.py # this harvest against the one it replaced
python3 corpus/squad.py --items=40 # one call per item, balanced across the two labels
python3 corpus/pages.py # mirror the pages into local/docs
python3 corpus/states.py --write # recover each docs example's state from its page
python3 corpus/decision.py --items=40 # the policy ablation, three calls per case
python3 corpus/advice.py --items=30 # does the advice improve the answer, two calls per arm
python3 corpus/rewrite.py # the same for a wording check, five arms
python3 corpus/wording.py --items=20 # three wording checks, seven arms
python3 corpus/families.py --items=6 # do the error-severity checks fire where answers fail
python3 corpus/levels.py # does a Score check's advice improve where reviews land
python3 corpus/levels.py --check=score/multi-dimension # with the arms in local/score-arms.json
decision.py fetches decision-v7 into local/ on first run, and advice.py, boundary.py,
families.py, planted.py and unread.py read it from there, so run decision.py before them.
The arm experiments - contradiction.py, rewrite.py, levels.py, wording.py - read their
sample and their rewrites from corpus/arms/, which is committed because those files are the
experiment's inputs rather than its output: the rewrites were written blind by a panel and cannot
be regenerated by running anything. A copy in local/ takes precedence, which is how a different
rewrite is tried without touching the committed one.
The checks with no gold negative are measured by planting the defect in material somebody else wrote, or by fetching a dataset that is the defect:
python3 corpus/planted.py --items=20 # a line addressed to the reviewer, put into a document
python3 corpus/unread.py --items=20 # a field added to a state that no question reads
python3 corpus/external.py --check=query/overlapping-questions --items=30 # Quora's pairs
python3 corpus/contradiction.py # criteria inverted against the instruction they belong to
python3 corpus/boundary.py # a boundary taken out of the policy that defines it
python3 corpus/encoded.py # the same comparison over a value written plainly and as hex
python3 corpus/dogfood.py # the catalogue's own wordings, as queries jevlint can read
decision.py is the strongest evidence here about a state-scoped check. decision-v7 ships states
holding a policy and a case, the question asked about them, and the answer it should get, so
removing the policy makes the state unable to answer the question and changes nothing else. It
also records what Jev answers as written, scored against the label, which is the baseline
advice.py, rewrite.py, wording.py and levels.py measure their repaired arms against.
squad.py is the only other measurement here whose labels come from outside this repository for a
state-scoped check. It fetches SQuAD 2.0 into local/ on first run, samples a seeded balanced
set so two runs measure the same material, sends each paragraph as the state and its question as
the question, and records what state/answer-absent read. Evidence
carries
the result.
harvest.py uses TYPESAFE_API_KEY from the environment. Pass an env file as its
argument, or set JEVLINT_ENV_FILE, if the key lives somewhere else; it refuses to start
instead of running every check against a file that is not there.
The three source files hold one question per entry with its label attached, which is the shape
a label is easiest to read and write in. build.py turns them into the query files jevlint
takes, grouped as they were asked, with the labels stripped and the ids normalised so that
measure.py can split a score id back into its file and its question. It refuses a source
entry whose primitive and criteria do not agree, naming the entry.
harvest.py runs jevlint check --all, which reports the model checks that ran and cleared
alongside the ones that fired. That records a probability whether or not it would have been
reported, without touching the catalogue other processes are reading.
Query files that carry a state are asked with it. states.py recovers the state each docs
example was run against from its own page, so 32 of the 76 entries carry one and the
state-scoped checks are measured on material somebody else wrote. The other 44 belong to
cookbook pages that fetch or loop over their state at run time, so the page holds none to
recover, and gold.json is questions quoted from prose with no material anywhere upstream.
Those files carry no state and the run says which checks that left out.
What the gold set is for
A check's own two examples show it can tell an obvious defect from an obvious non-defect. They cannot show it fires on the right things, because the person who wrote the check wrote them both. The gold set is labelled by somebody else: each entry quotes the documentation saying this question is right, or is wrong and why.
Fourteen are wrong with the defect named, so a check that claims to find that defect either finds it or does not. Eight are right.
A positive carries clears: the checks its citation speaks to. The docs call "Does
this message convey urgency?" a good question in a passage about snap judgements, which clears
question/compound-judgment and says nothing about where the question's boundary falls. A
blanket good label would have scored every check against a claim the citation does not make.
Six entries are tier: derived instead of quoted: the documentation defines the failure but
gives no example, so the example is written from the definition. They are weaker evidence than
the quoted ones and are marked so.
Reading the numbers
Some docs examples carry no criteria. They are extracted from code where the options and
levels are built from variables, so 34 of the 76 kept theirs and the rest are instruction only.
A check that reads criteria is asked about criteria that are not there on those, which is one
source of the firing rates on this tier.
The docs repeat examples across pages. 76 entries hold 52 distinct questions, so measure.py
counts each question once. Reading the raw entry count inflates every rate.
The clean-material column is the docs tier plus the gold positives. measure.py counts a
gold positive for a check only where that entry's clears names it, and no check is named by
more than two of the eight, so the column adds at most two to a denominator: 52 distinct docs
questions plus two gold positives is the 54 the widest checks are out of. The two tiers are
labelled clean by the same standard, and the column is a rate over everything labelled clean.
The field tier has no labels. A firing rate on it is a rate, not an error rate. Where a finding there has been checked by hand, Evidence says so.
A denominator is the questions the check was asked about. A check that applies only to
Choice questions is asked about the Choice questions and no others, so its rate is out of
those, not out of the tier. Counting a question the check never saw as a question it cleared
would turn silence into a clean bill of health, and measure.py prints the checks that were
never asked at all instead of scoring them 0.
Twenty-two gold entries is a small set, fourteen of them negatives. It is enough to show whether a check finds the example the documentation uses to define its own failure mode. It is not enough to put an interval on a recall figure.