wizard-work/ledger/CHECK-2026-11-02-jobs-table landed at2e57e6bas a Python program with a python3 shebang, so `bash <path>` -- the invocation the q143 sweep, verify.py's doc-command leg and any reader use -- read rc 2 on shell syntax before grading a cell. Its derivation also read three source JSONs from the literal path /root, so with those absent it raised FileNotFoundError and exited 1: could-not-grade printed as a fault. The grader moved to grade_jobs_table.py; the path keeps a bash wrapper. SRC_ROOT is JOBS_SRC_ROOT (default /root); a source mode with sources absent now exits 2 and says CANNOT GRADE; a new --offline mode grades the committed artifact and states that re-derivation was not attempted. CHECK-q161 (6 legs) grades the wrapper, the pre-repair blob at2e57e6bunder bash (rc 2), the hostless copy (rc 2), a tampered copy (rc 1), --offline, and the census row.
270 lines
10 KiB
Python
270 lines
10 KiB
Python
#!/usr/bin/env python3
|
|
"""grade_jobs_table.py -- the grader behind `CHECK-2026-11-02-jobs-table`.
|
|
|
|
WHY THIS FILE EXISTS (Q161, 2026-11-03)
|
|
The entry point at `wizard-work/ledger/CHECK-2026-11-02-jobs-table` used to be
|
|
this program with a `python3` shebang. Every caller in this tree invokes an
|
|
entry point with `bash` (the q143 sweep, `tools/verify.py`'s doc-command leg,
|
|
and any reader who types the path), so `bash CHECK-2026-11-02-jobs-table` read
|
|
rc 2 on shell syntax errors before grading a cell -- measured at `2e57e6b`:
|
|
`line 18: syntax error near unexpected token '('`. The grader moved here; the
|
|
path keeps a bash wrapper, which is the stable entry point.
|
|
|
|
Second defect, same record: the derivation read three source JSONs from the
|
|
literal host path `/root`. With those absent -- any clone, any other host --
|
|
the program died with `FileNotFoundError` and rc 1, i.e. "could not grade" was
|
|
reported as a fault. A missing source is not a failed artifact. It is now rc 2
|
|
and says so. The root is overridable with `JOBS_SRC_ROOT` so the absent case
|
|
can be exercised as a cell instead of argued about.
|
|
|
|
WHAT IT GRADES
|
|
default / --from-source the item's own claim: the committed `jobs.csv`
|
|
re-derives BYTE-FOR-BYTE from the source JSONs, and
|
|
its sha256 / row count / per-source counts /
|
|
fieldnames equal the frozen `manifest.json`. Plus the
|
|
required key fields are populated.
|
|
--offline the legs that do not need the sources: manifest
|
|
sha256 against the committed bytes, row count, header
|
|
vs manifest fieldnames, and each on-disk row's
|
|
required key fields non-empty. It says, in its own
|
|
output, that re-derivation was not attempted -- a
|
|
weaker reading is allowed, an unstated one is not.
|
|
|
|
EXIT CODES
|
|
0 every leg of the mode graded, all passed
|
|
1 a graded leg failed (drift, digest mismatch, empty required field)
|
|
2 could not grade: a source file is absent in a mode that needs it, a pinned
|
|
artifact is missing, or the manifest cannot be read. Never used for "the
|
|
artifact is wrong".
|
|
"""
|
|
import csv
|
|
import hashlib
|
|
import html
|
|
import io
|
|
import json
|
|
import os
|
|
import re
|
|
import sys
|
|
from typing import NoReturn
|
|
|
|
HERE = os.path.dirname(os.path.abspath(__file__))
|
|
MANIFEST = os.path.join(HERE, "manifest.json")
|
|
CSV = os.path.join(HERE, "jobs.csv")
|
|
|
|
# The three raw scrape files the table was built from. They are not tracked --
|
|
# they are the extraction lane's inputs -- so this path is a host fact, not a
|
|
# repository fact, and the check must be able to say it cannot grade without it.
|
|
SRC_FILES = {
|
|
"more_candidates.json": "more",
|
|
"ashby_candidates.json": "ashby",
|
|
"greenhouse_candidates.json": "greenhouse",
|
|
}
|
|
SRC_ROOT = os.environ.get("JOBS_SRC_ROOT", "/root")
|
|
|
|
TAG_RE = re.compile(r"<[^>]+>")
|
|
REQUIRED = ["source", "title", "job_url"]
|
|
|
|
|
|
def strip_html(s):
|
|
if not s:
|
|
return ""
|
|
s = html.unescape(s)
|
|
s = TAG_RE.sub(" ", s)
|
|
return " ".join(s.split())
|
|
|
|
|
|
def emp_of(rec):
|
|
for k in ("employmentType", "workplaceType"):
|
|
v = rec.get(k)
|
|
if v:
|
|
return v
|
|
return ""
|
|
|
|
|
|
def publish_at(rec):
|
|
return rec.get("publishedAt") or rec.get("updated_at") or ""
|
|
|
|
|
|
def desc_of(rec):
|
|
for k in ("descriptionPlain", "content", "descriptionHtml"):
|
|
v = rec.get(k)
|
|
if v:
|
|
return strip_html(v)[:220]
|
|
return ""
|
|
|
|
|
|
def build_expected_rows():
|
|
rows = []
|
|
counts = {}
|
|
for src_file, src_label in SRC_FILES.items():
|
|
path = os.path.join(SRC_ROOT, src_file)
|
|
with open(path) as fh:
|
|
data = json.load(fh)
|
|
counts[src_label] = 0
|
|
for rec in data:
|
|
rows.append({
|
|
"source": src_label,
|
|
"org": rec.get("org") or rec.get("board") or "",
|
|
"title": rec.get("title") or "",
|
|
"location": rec.get("location") or "",
|
|
"department": rec.get("department") or "",
|
|
"employment_type": emp_of(rec),
|
|
"job_url": rec.get("jobUrl") or rec.get("url") or "",
|
|
"apply_url": rec.get("applyUrl") or "",
|
|
"published_at": publish_at(rec),
|
|
"description": desc_of(rec),
|
|
})
|
|
counts[src_label] += 1
|
|
return rows, counts
|
|
|
|
|
|
def csv_bytes(rows):
|
|
buf = io.StringIO()
|
|
w = csv.DictWriter(buf, fieldnames=list(rows[0].keys()) if rows else [])
|
|
w.writeheader()
|
|
w.writerows(rows)
|
|
return buf.getvalue().encode("utf-8")
|
|
|
|
|
|
def cannot_grade(msg: str) -> NoReturn:
|
|
print("CANNOT GRADE: " + msg)
|
|
sys.exit(2)
|
|
|
|
|
|
def missing_sources():
|
|
return [f for f in SRC_FILES if not os.path.isfile(os.path.join(SRC_ROOT, f))]
|
|
|
|
|
|
def main(argv):
|
|
mode = "from-source"
|
|
for arg in argv:
|
|
if arg == "--offline":
|
|
mode = "offline"
|
|
elif arg in ("--from-source", "--default"):
|
|
mode = "from-source"
|
|
elif arg in ("-h", "--help"):
|
|
print(__doc__)
|
|
return 0
|
|
else:
|
|
print("unknown argument: %s" % arg)
|
|
return 2
|
|
|
|
if not os.path.isfile(MANIFEST):
|
|
cannot_grade("manifest.json is missing beside this grader")
|
|
if not os.path.isfile(CSV):
|
|
cannot_grade("jobs.csv is missing beside this grader")
|
|
try:
|
|
with open(MANIFEST) as fh:
|
|
manifest = json.load(fh)
|
|
except (ValueError, OSError) as ex:
|
|
cannot_grade("manifest.json is unreadable: %r" % ex)
|
|
|
|
if mode == "from-source":
|
|
absent = missing_sources()
|
|
if absent:
|
|
cannot_grade(
|
|
"source JSON(s) absent under %s: %s -- the byte-for-byte "
|
|
"re-derivation cannot be graded here; `--offline` grades the "
|
|
"committed artifact alone and says so"
|
|
% (SRC_ROOT, ", ".join(sorted(absent))))
|
|
|
|
ok = True
|
|
failures = []
|
|
with open(CSV, "rb") as fh:
|
|
on_disk_bytes = fh.read()
|
|
disk_sha = hashlib.sha256(on_disk_bytes).hexdigest()
|
|
text = on_disk_bytes.decode("utf-8", "surrogateescape")
|
|
n_data = text.count("\n") - 1 # minus header
|
|
|
|
# --- legs 1-3: the manifest's facts against the committed bytes ----------
|
|
sha_ok = disk_sha == manifest.get("sha256")
|
|
if not sha_ok:
|
|
ok = False
|
|
failures.append("sha256 mismatch: on-disk=%s manifest=%s"
|
|
% (disk_sha, manifest.get("sha256")))
|
|
|
|
count_ok = manifest.get("row_count") == n_data
|
|
if not count_ok:
|
|
ok = False
|
|
failures.append("row_count mismatch: manifest=%s ondisk_data=%s"
|
|
% (manifest.get("row_count"), n_data))
|
|
|
|
reader = csv.DictReader(io.StringIO(text))
|
|
fields = reader.fieldnames
|
|
fields_ok = fields == manifest.get("fieldnames")
|
|
if not fields_ok:
|
|
ok = False
|
|
failures.append("fieldnames mismatch: %s" % fields)
|
|
|
|
disk_rows = list(reader)
|
|
unreadable = [i for i, r in enumerate(disk_rows)
|
|
if r is None or any(not (r.get(k) or "").strip() for k in REQUIRED)]
|
|
if unreadable:
|
|
ok = False
|
|
failures.append("rows with empty required field: %s" % unreadable[:5])
|
|
|
|
derived_rows = None
|
|
deriv_ok = None
|
|
if mode == "from-source":
|
|
# --- legs 4-5: re-derivation from the pinned sources -----------------
|
|
derived_rows, derived_counts = build_expected_rows()
|
|
derived_bytes = csv_bytes(derived_rows)
|
|
deriv_ok = on_disk_bytes == derived_bytes
|
|
if not deriv_ok:
|
|
ok = False
|
|
failures.append(
|
|
"jobs.csv bytes differ from a fresh re-derivation from source "
|
|
"(on-disk %d bytes, derived %d bytes)"
|
|
% (len(on_disk_bytes), len(derived_bytes)))
|
|
o = text.splitlines()
|
|
e = derived_bytes.decode("utf-8", "surrogateescape").splitlines()
|
|
for i in range(max(len(o), len(e))):
|
|
a = o[i] if i < len(o) else "<missing>"
|
|
b = e[i] if i < len(e) else "<missing>"
|
|
if a != b:
|
|
failures.append("line %d: ondisk=%r derived=%r"
|
|
% (i, a[:60], b[:60]))
|
|
break
|
|
if derived_counts != manifest.get("counts"):
|
|
ok = False
|
|
failures.append("counts mismatch: manifest=%s derived=%s"
|
|
% (manifest.get("counts"), derived_counts))
|
|
if len(derived_rows) != n_data:
|
|
ok = False
|
|
failures.append("derived_rows %d != on-disk rows %d"
|
|
% (len(derived_rows), n_data))
|
|
|
|
# ---- measured report ----------------------------------------------------
|
|
print("CHECK-2026-11-02-jobs-table [%s]" % mode)
|
|
print(" source_root: %s%s"
|
|
% (SRC_ROOT, "" if mode == "from-source" else " (not read in this mode)"))
|
|
if mode == "from-source":
|
|
print(" source_files: %s"
|
|
% ", ".join("%s=%d" % kv for kv in sorted(derived_counts.items())))
|
|
print(" derived_rows: %d" % len(derived_rows))
|
|
print(" byte_identical: %s" % deriv_ok)
|
|
else:
|
|
print(" re_derivation: NOT ATTEMPTED (--offline: the sources are not read)")
|
|
print(" manifest.row_count: %s" % manifest.get("row_count"))
|
|
print(" on-disk_rows: %d" % n_data)
|
|
print(" sha256: %s" % disk_sha)
|
|
print(" csv_bytes: %d" % len(on_disk_bytes))
|
|
print(" fieldnames_match: %s" % fields_ok)
|
|
print(" required_fields_populated: %d/%d" % (len(disk_rows) - len(unreadable),
|
|
len(disk_rows)))
|
|
|
|
if ok:
|
|
print("VERDICT: PASS (%s)"
|
|
% ("jobs.csv re-derives from source and matches manifest"
|
|
if mode == "from-source"
|
|
else "committed artifact matches its manifest; source derivation not attempted"))
|
|
return 0
|
|
print("VERDICT: FAIL")
|
|
for f in failures:
|
|
print(" - " + f)
|
|
return 1
|
|
|
|
|
|
if __name__ == "__main__":
|
|
sys.exit(main(sys.argv[1:]))
|