provenance:programming:crawler:webxray:random_sample
Differences
This shows you the differences between two versions of the page.
| Both sides previous revisionPrevious revisionNext revision | Previous revision | ||
| provenance:programming:crawler:webxray:random_sample [2026/09/11 16:02] – Inter-rater re-adjudication (2026-09-11): add sections AA-AK (subsample draw, kappa estimator, citation corrections, per-page figures guard and its mutation harness, six unedited outputs); sections now emit in letter order; owner_verify_sources.py normali karel.kubicek.claude | provenance:programming:crawler:webxray:random_sample [2026/09/17 07:24] (current) – Markup rendering sweep: wrap escapes, CLI flag typography, and heading quotes. Authored by Claude. karel.kubicek.claude | ||
|---|---|---|---|
| Line 7: | Line 7: | ||
| **inter-rater re-adjudication of 2026-09-11** (sections AA–AK), which measured | **inter-rater re-adjudication of 2026-09-11** (sections AA–AK), which measured | ||
| how far a second model reading the same brief reaches the same verdicts. | how far a second model reading the same brief reaches the same verdicts. | ||
| + | |||
| + | The **second pass over the residue**, run later the same day — which | ||
| + | re-adjudicated the 40 domains this sample could not settle, moved 28 of them | ||
| + | into the rates, and changed the content page's headline table and its Fisher | ||
| + | table — has its own appendix, for exactly the reason given below: | ||
| + | [[provenance: | ||
| + | 343 kB, most of it one probe log, and adding them here would have made this page | ||
| + | 628 kB. That page computes both figures when it is generated, from the same file | ||
| + | lists both manifests use. | ||
| Read [[provenance: | Read [[provenance: | ||
| Line 17: | Line 26: | ||
| AF–AK their outputs. | AF–AK their outputs. | ||
| - | **Why this is a separate page.** These blocks are 271 kB, and the parent | + | **Why this is a separate page.** These blocks are 290 kB, and the parent |
| provenance page is already large without them. Raw size alone would not decide | provenance page is already large without them. Raw size alone would not decide | ||
| it — '' | it — '' | ||
| Line 48: | Line 57: | ||
| | C | '' | | C | '' | ||
| | D | '' | | D | '' | ||
| - | | E | '' | + | | E | '' |
| | F | '' | | F | '' | ||
| - | | G | '' | + | | G | '' |
| | H | '' | | H | '' | ||
| | I | '' | | I | '' | ||
| Line 61: | Line 70: | ||
| | P | '' | | P | '' | ||
| | Q | '' | | Q | '' | ||
| - | | R | '' | + | | R | '' |
| | S | '' | | S | '' | ||
| | T | '' | | T | '' | ||
| Line 71: | Line 80: | ||
| | Z | '' | | Z | '' | ||
| | AA | '' | | AA | '' | ||
| - | | AB | '' | + | | AB | '' |
| - | | AC | '' | + | | AC | '' |
| - | | AD | '' | + | | AD | '' |
| - | | AE | '' | + | | AE | '' |
| | AF | '' | | AF | '' | ||
| | AG | '' | | AG | '' | ||
| - | | AH | '' | + | | AH | '' |
| - | | AI | '' | + | | AI | '' |
| - | | AJ | '' | + | | AJ | '' |
| - | | AK | '' | + | | AK | '' |
| ===== A. scripts/ | ===== A. scripts/ | ||
| Line 552: | Line 561: | ||
| OK the quote (or, for a long quote, a 60-character window of it) | OK the quote (or, for a long quote, a 60-character window of it) | ||
| appears in the fetched bytes after normalisation | appears in the fetched bytes after normalisation | ||
| + | OK-MARKUP the quote is not in the page's *text* but is in its markup -- a | ||
| + | citation to a `<script src=...> | ||
| + | text normaliser strips tags, so such a quote can never be found in | ||
| + | the haystack it builds; checking the raw bytes as well is what | ||
| + | distinguishes "the evidence is not there" from "this checker cannot | ||
| + | see that kind of evidence" | ||
| + | the browser pass, both returned NOTFOUND for a `<script src>` line | ||
| + | that is verbatim in kameleoon.com/ | ||
| + | disqualification that had already been acted on. | ||
| NOTFOUND | NOTFOUND | ||
| FETCHFAIL the URL could not be fetched from this host <- needs a human look, | FETCHFAIL the URL could not be fetched from this host <- needs a human look, | ||
| Line 563: | Line 581: | ||
| - | def norm(s): | + | def norm(s, strip_tags=True): |
| s = s.lower() | s = s.lower() | ||
| # Modern sites ship their page text inside an escaped JSON payload, so the | # Modern sites ship their page text inside an escaped JSON payload, so the | ||
| Line 575: | Line 593: | ||
| s = s.replace("& | s = s.replace("& | ||
| s = s.replace("& | s = s.replace("& | ||
| - | s = re.sub(r"< | + | |
| + | | ||
| # Every remaining entity becomes a space on BOTH sides, or "& | # Every remaining entity becomes a space on BOTH sides, or "& | ||
| # page and " | # page and " | ||
| Line 593: | Line 612: | ||
| s = re.sub(r" | s = re.sub(r" | ||
| return re.sub(r" | return re.sub(r" | ||
| + | |||
| + | |||
| + | def decode(body): | ||
| + | """ | ||
| + | |||
| + | Decoding as UTF-8 with ``errors=" | ||
| + | windows-1251 or shift_jis page into U+FFFD, so a Cyrillic or Japanese quote | ||
| + | can never match and the row is reported NOTFOUND -- a fabrication verdict on | ||
| + | a correct citation. The normaliser was already fixed once for this class of | ||
| + | bug (it used to delete non-Latin letters); this is the same bug one layer | ||
| + | earlier, found on 2026-09-11 when ``help.i.ua/ | ||
| + | page declares ``charset=windows-1251`` and every Ukrainian word in it | ||
| + | decoded to replacement characters. | ||
| + | |||
| + | Sniffed from the bytes rather than from the HTTP header, because the header | ||
| + | is not cached -- the cache holds the body alone. If nothing is declared, or | ||
| + | the declared codec is unknown, the old behaviour is the fallback. | ||
| + | """ | ||
| + | m = re.search(rb""" | ||
| + | if m: | ||
| + | enc = m.group(1).decode(" | ||
| + | if enc.lower().replace(" | ||
| + | try: | ||
| + | return body.decode(enc) | ||
| + | except (LookupError, | ||
| + | pass | ||
| + | return body.decode(" | ||
| Line 613: | Line 659: | ||
| except Exception as e: | except Exception as e: | ||
| return None, f"tls: {e}" | return None, f"tls: {e}" | ||
| + | os.makedirs(cache, | ||
| + | open(path, " | ||
| + | return out, None | ||
| + | if url.startswith(" | ||
| + | # The 2026-09-11 second pass over the residue cites registry and | ||
| + | # registrar WHOIS records, which are not reachable over HTTP: the | ||
| + | # protocol is a port-43 socket. Without this branch every such row | ||
| + | # FETCHFAILs, which reads as an unverifiable citation when in fact the | ||
| + | # checker had no client. The URL is `whois://< | ||
| + | # query is re-issued against the *named server*, not re-resolved | ||
| + | # through IANA, so the row is checked against the record it cites. | ||
| + | rest = url[len(" | ||
| + | server, _, dom = rest.partition("/" | ||
| + | if not dom: | ||
| + | return None, " | ||
| + | sys.path.insert(0, | ||
| + | import time | ||
| + | import whois43 | ||
| + | out = b"" | ||
| + | # Two attempts, 20 s apart. One retry is enough: the observed limit is a | ||
| + | # burst limit, and both stubs in the first run answered correctly 25 s | ||
| + | # later. Retrying forever would turn a registrar' | ||
| + | err = None | ||
| + | for attempt in range(2): | ||
| + | if attempt: | ||
| + | time.sleep(20) | ||
| + | try: | ||
| + | out = whois43.ask(server, | ||
| + | except Exception as e: | ||
| + | # A raised exception is a transport failure too -- a reset or a | ||
| + | # timeout is exactly what a rate limiter does -- so it must take | ||
| + | # the retry as well. It did not until 2026-09-11, when a review | ||
| + | # pass read the comment above ("two attempts, 20 s apart" | ||
| + | # then read the code, which returned on the first exception. | ||
| + | err, out = e, b"" | ||
| + | continue | ||
| + | err = None | ||
| + | if len(out) >= 400 and dom.encode().lower() in out.lower(): | ||
| + | break | ||
| + | if err is not None: | ||
| + | return None, f" | ||
| + | # Registrar WHOIS servers rate-limit. MarkMonitor answers the fourth | ||
| + | # rapid query with a stub that has no registrant block, and caching that | ||
| + | # stub turns a correct citation into a permanent NOTFOUND: it happened | ||
| + | # to ``sa-as.com`` and ``blogblog.com`` in this script' | ||
| + | # 2026-09-11, and both verified on a retry 25 seconds later. A short or | ||
| + | # domain-less answer is a transport failure, not evidence, so it is | ||
| + | # neither returned nor cached. | ||
| + | if len(out) < 400 or dom.encode().lower() not in out.lower(): | ||
| + | return None, (f" | ||
| + | f" | ||
| os.makedirs(cache, | os.makedirs(cache, | ||
| open(path, " | open(path, " | ||
| Line 664: | Line 761: | ||
| " | " | ||
| continue | continue | ||
| - | hay = norm(body.decode(" | + | hay = norm(decode(body)) |
| nq = norm(quote) | nq = norm(quote) | ||
| # A quote the adjudicator elided ("A ... B") is checked fragment by | # A quote the adjudicator elided ("A ... B") is checked fragment by | ||
| Line 671: | Line 768: | ||
| frags = [norm(f) for f in re.split(r" | frags = [norm(f) for f in re.split(r" | ||
| frags = [f for f in frags if len(f) >= 12] | frags = [f for f in frags if len(f) >= 12] | ||
| + | # The same normalisation WITHOUT the tag strip, so a quote that is | ||
| + | # itself markup can be found. Kept separate rather than merged into | ||
| + | # `hay`: a prose quote must match the page's text, and dropping the tag | ||
| + | # strip for every row would let a quote match an HTML comment or an | ||
| + | # attribute it was never on. | ||
| + | raw_hay = norm(decode(body), | ||
| if not nq: | if not nq: | ||
| status = " | status = " | ||
| Line 677: | Line 780: | ||
| elif len(frags) > 1 and all(f in hay for f in frags): | elif len(frags) > 1 and all(f in hay for f in frags): | ||
| status = " | status = " | ||
| + | elif nq in raw_hay: | ||
| + | status = " | ||
| else: | else: | ||
| # long quotes get spliced by boilerplate; | # long quotes get spliced by boilerplate; | ||
| Line 767: | Line 872: | ||
| </ | </ | ||
| - | ===== G. scripts/ | + | ===== G. scripts/ |
| Every row a machine flagged and a human then re-checked, with the action taken and why: '' | Every row a machine flagged and a human then re-checked, with the action taken and why: '' | ||
| Line 785: | Line 890: | ||
| quotation marks" failure, and it happened in 4 of 175 rows despite an | quotation marks" failure, and it happened in 4 of 175 rows despite an | ||
| instruction that said verbatim -- which is the argument for verifying every | instruction that said verbatim -- which is the argument for verifying every | ||
| - | citation by machine rather than trusting the adjudicator. | + | citation by machine rather than trusting the adjudicator. A fifth requote, |
| + | ``compass-fit.jp``, | ||
| + | dropped character. It is counted with them because the remedy is the same, | ||
| + | not because it is the same defect. It was | ||
| + | found on 2026-09-11 and applied on 2026-09-17, by re-running a *fixed* | ||
| + | checker over rows the broken one had passed: ``norm()`` used to delete every non-Latin character, | ||
| + | so ``compass-fit.jp``' | ||
| + | letters ``COMPASS-FIT`` it happens to contain. A checker that cannot see a | ||
| + | script cannot police it, and every row it passed in that script is | ||
| + | unverified until it is re-run. | ||
| * ``resource`` -- the cited URL was the wrong page (a 404 template, a | * ``resource`` -- the cited URL was the wrong page (a 404 template, a | ||
| JS-only route, a blocked host). A different primary source for the same | JS-only route, a blocked host). A different primary source for the same | ||
| Line 855: | Line 969: | ||
| " | " | ||
| "error needs a source naming the real owner and there is none." | "error needs a source naming the real owner and there is none." | ||
| + | " | ||
| + | " | ||
| + | " | ||
| + | "found 2026-09-11, applied 2026-09-17. The norm() character-class fix " | ||
| + | "of 2026-09-11 was re-run that day over all 175 rows of the first " | ||
| + | " | ||
| + | "Latin letters COMPASS-FIT its Japanese quote happens to contain. The " | ||
| + | " | ||
| + | " | ||
| + | " | ||
| + | " | ||
| + | " | ||
| + | " | ||
| + | " | ||
| + | " | ||
| + | " | ||
| + | "the operating subsidiary together, which is what owner_today = " | ||
| + | " | ||
| + | " | ||
| + | " | ||
| + | " | ||
| } | } | ||
| Line 907: | Line 1042: | ||
| </ | </ | ||
| - | ===== H. scripts/ | + | ===== H. scripts/ |
| The methodological correction made during the run, kept rather than smoothed away. Each of the 26 re-scores carries the entity string, the adjudicated owner and the reason. | The methodological correction made during the run, kept rather than smoothed away. Each of the 26 re-scores carries the entity string, the adjudicated owner and the reason. | ||
| Line 2138: | Line 2273: | ||
| ===== R. Unedited output: scripts/ | ===== R. Unedited output: scripts/ | ||
| - | Run as '' | + | Run as '' |
| < | < | ||
| - | 10 corrections applied to 175 rows, after machine verification of every cited source: demote=1, requote=4, resource=5 | + | 11 corrections applied to 175 rows, after machine verification of every cited source: demote=1, requote=5, resource=5 |
| twiago.com | twiago.com | ||
| Line 2192: | Line 2327: | ||
| was quote : Condé Nast Entertainment | was quote : Condé Nast Entertainment | ||
| now source: None | now source: None | ||
| + | |||
| + | compass-fit.jp | ||
| + | was source: https:// | ||
| + | was quote : 台湾国内でのネイティブ型広告配信サービス「COMPASS-FIT」の提供を開始致しました。 | ||
| + | now source: https:// | ||
| wrote out/ | wrote out/ | ||
| Line 2198: | Line 2338: | ||
| ===== S. Unedited output: scripts/ | ===== S. Unedited output: scripts/ | ||
| - | Run as '' | + | Run as '' |
| < | < | ||
| Line 2299: | Line 2439: | ||
| ===== V. Unedited output: scripts/ | ===== V. Unedited output: scripts/ | ||
| - | Run as '' | + | Run as '' |
| < | < | ||
| Line 4505: | Line 4645: | ||
| ALL all 35 rows, raw vocabulary. | ALL all 35 rows, raw vocabulary. | ||
| | | ||
| - | NAMED rows where the list file names an owner, i.e. neither rater is | + | NAMED rows where the list file names an owner. |
| - | forced to `absent`. | + | from the list file by `owner_merge_rows.py` rather than judged, so |
| - | asks a rater to judge. | + | the two raters' |
| + | | ||
| + | the equivalence rather than assuming it. | ||
| + | | ||
| + | move between 2026-08-17 and 2026-09-05 -- the sets could diverge, | ||
| + | and an `or` selection would then silently pair a real verdict | ||
| + | | ||
| ADJUDICABLE | ADJUDICABLE | ||
| | | ||
| Line 4530: | Line 4676: | ||
| kappa = (po - pe) / (1 - pe), pe = sum_c p1(c) * p2(c) | kappa = (po - pe) / (1 - pe), pe = sum_c p1(c) * p2(c) | ||
| - | Intervals are bootstrap percentiles over **domains** | + | Intervals are bootstrap percentiles over **domains** |
| + | over **cells** for the pooled rows -- a pooled row has already flattened the | ||
| + | three lists' verdicts into one sequence, so its resample can draw two cells of | ||
| + | one domain independently and its interval is narrower than it should be. That is | ||
| + | stated on the page as well. 10,000 resamples, | ||
| normal approximation: | normal approximation: | ||
| variance is not usable. | variance is not usable. | ||
| Line 4559: | Line 4709: | ||
| po = sum(1 for a, b in pairs if a == b) / n | po = sum(1 for a, b in pairs if a == b) / n | ||
| m1, m2 = Counter(a for a, _ in pairs), Counter(b for _, b in pairs) | m1, m2 = Counter(a for a, _ in pairs), Counter(b for _, b in pairs) | ||
| - | pe = sum(m1[c] / n * m2[c] / n for c in set(m1) | set(m2)) | + | |
| + | # so the float addition order varied between runs and the JSON artefact' | ||
| + | # 15th-16th decimal moved. No published 3-decimal figure ever changed, but a | ||
| + | # " | ||
| + | | ||
| if abs(1 - pe) < 1e-12: | if abs(1 - pe) < 1e-12: | ||
| return {" | return {" | ||
| Line 4597: | Line 4751: | ||
| s += f" | s += f" | ||
| return s | return s | ||
| + | |||
| + | |||
| + | def assert_absent_agrees(doms, | ||
| + | """ | ||
| + | exactly the same cells. Every population below is selected with `or`, which | ||
| + | is only equivalent to the docstring' | ||
| + | while that holds. Checked, not assumed.""" | ||
| + | bad = [(d, lab, R1[d][key], R2[d][key]) | ||
| + | for lab, key in LISTS for d in doms | ||
| + | if (R1[d][key] == " | ||
| + | if bad: | ||
| + | lines = " | ||
| + | raise SystemExit( | ||
| + | f" | ||
| + | "from the list file, not judged, so this means the two raters were " | ||
| + | " | ||
| + | "would be wrong.\n" | ||
| + | print(f" | ||
| + | "list file, not a judgement) -- the NAMED/ | ||
| Line 4617: | Line 4790: | ||
| if missing: | if missing: | ||
| raise SystemExit(f" | raise SystemExit(f" | ||
| + | assert_absent_agrees(doms, | ||
| print(" | print(" | ||
| Line 4861: | Line 5035: | ||
| | | ||
| the gap is the brief' | the gap is the brief' | ||
| - | kameleoon.io NOTFOUND in bytes and NOTFOUND in the browser. The quote is a | + | kameleoon.io NOTFOUND in bytes and NOTFOUND in the browser |
| - | | + | is sound. **This row was demoted and the demotion was wrong**, and |
| - | | + | it is recorded here rather than quietly reversed. The quote is a |
| - | instrument. **Demoted to unresolved**, | + | |
| - | first sitting wrote and applied to ``cnevids.com``. | + | to markup: ``owner_verify_sources.norm()`` strips ``< |
| + | | ||
| + | | ||
| + | | ||
| + | | ||
| + | | ||
| + | ``OK-MARKUP`` status and the row stands. | ||
| - | Demoting changes one (domain, list) verdict, so the inter-rater figures are | + | **No row is demoted.** All three machine failures turned out to be limits of the |
| - | reported both ways and the difference | + | instruments rather than bad citations, which is itself |
| + | three would have been acted on without a hand check, and one of them was. The | ||
| + | inter-rater figures are therefore identical before | ||
| + | that equality | ||
| """ | """ | ||
| import argparse, json, sys | import argparse, json, sys | ||
| Line 4882: | Line 5065: | ||
| "A port-43 query to whois.nic.it returns the quoted line verbatim. Row " | "A port-43 query to whois.nic.it returns the quoted line verbatim. Row " | ||
| " | " | ||
| - | " | + | " |
| - | " | + | " |
| - | "unresolved under the first sitting' | + | "a `<script src>` line, and both the bytes pass (which strips tags) and " |
| - | "becomes unknown, and webXray/ | + | "the browser pass (which reads innerText) are blind to markup by " |
| - | "a fact about the file."), | + | " |
| + | " | ||
| } | } | ||
| Line 4929: | Line 5113: | ||
| print() | print() | ||
| print(f" | print(f" | ||
| + | # The page states that the corrected and uncorrected inter-rater figures are | ||
| + | # the same. That is only true while nothing is demoted, so it is checked | ||
| + | # here rather than left as a sentence somebody has to remember. | ||
| + | if changed: | ||
| + | print(" | ||
| + | "from the uncorrected ones and both must be published." | ||
| json.dump(rows, | json.dump(rows, | ||
| print(f" | print(f" | ||
| Line 5052: | Line 5242: | ||
| ap.add_argument(" | ap.add_argument(" | ||
| ap.add_argument(" | ap.add_argument(" | ||
| + | ap.add_argument(" | ||
| + | help=" | ||
| + | " | ||
| + | " | ||
| args = ap.parse_args() | args = ap.parse_args() | ||
| Line 5086: | Line 5280: | ||
| fails.append(f" | fails.append(f" | ||
| " | " | ||
| + | |||
| + | # Figures that live only in the scripts' | ||
| + | # A reviewer mutated each of these on the page and this guard survived every | ||
| + | # one, because it only ever read the JSON. They are parsed out of the two | ||
| + | # committed outputs so they are checked against the tool, not retyped here. | ||
| + | kout = out | ||
| + | vout = open(args.verify_output, | ||
| + | |||
| + | def need(label, value, where): | ||
| + | counter[0] += 1 | ||
| + | if value not in where: | ||
| + | fails.append(f" | ||
| + | |||
| + | # section 0: how many cells each rater scored `self-named` | ||
| + | m = re.search(r" | ||
| + | r"both on the same cell (\d+)", | ||
| + | if not m: | ||
| + | fails.append(" | ||
| + | else: | ||
| + | r1, r2, both = m.groups() | ||
| + | # Bare digits are useless needles -- " | ||
| + | # are deliberately coupled to the sentence' | ||
| + | # sentence breaks the guard instead of silently unchecking the figure. | ||
| + | # Checked wherever the sentence SHAPE occurs, on every page, rather | ||
| + | # than as a needle in the union: both pages carry this sentence, so | ||
| + | # corrupting one copy left the other to satisfy a union check. | ||
| + | counter[0] += 1 | ||
| + | seen = 0 | ||
| + | for role, text in sorted(pages.items()): | ||
| + | for mm in re.finditer(r" | ||
| + | seen += 1 | ||
| + | if mm.group(1) != r2: | ||
| + | fails.append(f" | ||
| + | | ||
| + | if not seen: | ||
| + | fails.append(" | ||
| + | need(" | ||
| + | need(" | ||
| + | |||
| + | # section 2b: the sourcing-vs-reading split, per list and pooled | ||
| + | for line in kout.splitlines(): | ||
| + | m = re.match(r" | ||
| + | | ||
| + | | ||
| + | if m: | ||
| + | lab, n, ag, so, rd = m.groups() | ||
| + | # Whole-row match, not four bare digits: " | ||
| + | # over these pages, so per-cell needles passed a mutated table. | ||
| + | name = " | ||
| + | row = (r" | ||
| + | | ||
| + | | ||
| + | counter[0] += 1 | ||
| + | if not re.search(row, | ||
| + | fails.append(f" | ||
| + | | ||
| + | m = re.search(r" | ||
| + | if not m: | ||
| + | fails.append(" | ||
| + | else: | ||
| + | need(" | ||
| + | |||
| + | # section 5: row-level resolution counts | ||
| + | m = re.search(r" | ||
| + | r" | ||
| + | if not m: | ||
| + | fails.append(" | ||
| + | else: | ||
| + | a, n, b, same = m.groups() | ||
| + | need(" | ||
| + | need(" | ||
| + | need(" | ||
| + | |||
| + | # section 6: the unweighted current-share table, both raters, all three lists | ||
| + | for line in kout.splitlines(): | ||
| + | m = re.match(r" | ||
| + | | ||
| + | if m: | ||
| + | lab, a, b = m.groups() | ||
| + | need(f" | ||
| + | need(f" | ||
| + | |||
| + | # the citation tally, from owner_verify_sources.py' | ||
| + | m = re.search(r" | ||
| + | if not m: | ||
| + | fails.append(" | ||
| + | else: | ||
| + | for part in m.group(1).split(", | ||
| + | k_, v_ = part.split(" | ||
| + | need(f" | ||
| # A three-decimal value in kappa context that neither JSON produces is a | # A three-decimal value in kappa context that neither JSON produces is a | ||
| Line 5136: | Line 5420: | ||
| JC=out/ | JC=out/ | ||
| OUT=out/ | OUT=out/ | ||
| + | VOUT=out/ | ||
| TMP=$(mktemp -d) | TMP=$(mktemp -d) | ||
| fails=0 | fails=0 | ||
| Line 5154: | Line 5439: | ||
| else roles=(--page " | else roles=(--page " | ||
| if python3 scripts/ | if python3 scripts/ | ||
| - | | + | |
| + | | ||
| echo " | echo " | ||
| fails=$((fails+1)) | fails=$((fails+1)) | ||
| Line 5170: | Line 5456: | ||
| mutate "prov: ADJUDICABLE pooled +0.680 -> +0.690" | mutate "prov: ADJUDICABLE pooled +0.680 -> +0.690" | ||
| mutate "prov: current-vs-rest pooled +0.708 -> +0.718" | mutate "prov: current-vs-rest pooled +0.708 -> +0.718" | ||
| - | mutate " | + | # The corrected-variant figures are currently EQUAL to the uncorrected ones, |
| + | # because owner_irr_corrections.py demotes nothing. Mutating a value that is no | ||
| + | # longer on the page is a no-op, and this harness reports a no-op mutation as a | ||
| + | # survivor rather than as a pass -- which is how this line was caught after the | ||
| + | # kameleoon.io demotion was reversed. These two target figures that are on the | ||
| + | # page today and are not covered by the kappa mutations above. | ||
| + | mutate " | ||
| + | mutate "prov: current-vs-rest agreement 87.2% -> 88.2%" | ||
| mutate "prov: invent an unsourced kappa of 0.842" | mutate "prov: invent an unsourced kappa of 0.842" | ||
| + | # Figures that live only in the scripts' | ||
| + | # one of these and the guard survived, because it only read the kappa JSON. | ||
| + | mutate "prov: self-named rater2 6 -> 9 cells" | ||
| + | mutate "prov: split table sourcing 17 -> 20" | ||
| + | mutate "prov: sourcing headline 63.0% -> 70.0%" | ||
| + | mutate "prov: rater-1 unresolved 10 of 35 -> 13 of 35" | ||
| + | mutate "prov: webXray current-share 70.0% (7\/ | ||
| + | mutate "prov: citation tally OK 30 -> OK 32" | ||
| mutate "page: pooled kappa +0.400 -> +0.410" | mutate "page: pooled kappa +0.400 -> +0.410" | ||
| mutate "page: Disconnect NAMED kappa +0.460 -> +0.461" | mutate "page: Disconnect NAMED kappa +0.460 -> +0.461" | ||
| Line 5186: | Line 5487: | ||
| ===== AF. Unedited output: scripts/ | ===== AF. Unedited output: scripts/ | ||
| - | Run as '' | + | Run as '' |
| < | < | ||
| Line 5210: | Line 5511: | ||
| ===== AG. Unedited output: scripts/ | ===== AG. Unedited output: scripts/ | ||
| - | Run as '' | + | Run as '' |
| < | < | ||
| Line 5222: | Line 5523: | ||
| ===== AH. Unedited output: scripts/ | ===== AH. Unedited output: scripts/ | ||
| - | Run as '' | + | Run as '' |
| < | < | ||
| Line 5240: | Line 5541: | ||
| OK | OK | ||
| OK | OK | ||
| - | NOTFOUND | + | OK-MARKUP |
| OK | OK | ||
| OK | OK | ||
| Line 5257: | Line 5558: | ||
| OK | OK | ||
| - | Verification of cited primary sources: FETCHFAIL=2, | + | Verification of cited primary sources: FETCHFAIL=2, |
| </ | </ | ||
| ===== AI. Unedited output: scripts/ | ===== AI. Unedited output: scripts/ | ||
| - | Run as '' | + | Run as '' |
| < | < | ||
| Line 5269: | Line 5570: | ||
| KEEP cedscdn.it | KEEP cedscdn.it | ||
| FETCHFAIL is an unsupported URL scheme: `whois:// | FETCHFAIL is an unsupported URL scheme: `whois:// | ||
| - | DEMOTE | + | KEEP |
| - | NOTFOUND in bytes and NOTFOUND in a rendered browser. Demoted to unresolved under the first sitting' | + | NOTFOUND in both instruments was a FALSE disqualification: |
| - | disconnect: current | + | |
| - | rows examined: 3; verdict cells changed: | + | rows examined: 3; verdict cells changed: |
| wrote out/ | wrote out/ | ||
| </ | </ | ||
| Line 5279: | Line 5579: | ||
| ===== AJ. Unedited output: scripts/ | ===== AJ. Unedited output: scripts/ | ||
| - | Run as '' | + | Run as '' |
| < | < | ||
| + | `absent` agrees on all 105 cells (it is a fact about the list file, not a judgement) -- the NAMED/ | ||
| ============================================================================== | ============================================================================== | ||
| INTER-RATER AGREEMENT, OWNERSHIP ADJUDICATION | INTER-RATER AGREEMENT, OWNERSHIP ADJUDICATION | ||
| Line 5484: | Line 5785: | ||
| caught | caught | ||
| caught | caught | ||
| - | caught | + | caught |
| + | caught | ||
| caught | caught | ||
| + | caught | ||
| + | caught | ||
| + | caught | ||
| + | caught | ||
| + | caught | ||
| + | caught | ||
| caught | caught | ||
| caught | caught | ||
| Line 5497: | Line 5805: | ||
| * [[provenance: | * [[provenance: | ||
| * [[programming: | * [[programming: | ||
| + | * [[provenance: | ||
| * [[literature: | * [[literature: | ||
| + | |||
| + | ===== Markup sweep, 2026-09-17 ===== | ||
| + | |||
| + | Mechanical rendering repair only: a fresh live raw/XHTML export of 188 pages was checked with '' | ||
provenance/programming/crawler/webxray/random_sample.1789142548.txt.gz · Last modified: by karel.kubicek.claude
