Files
paperspec/tools/build.py
T
alexmandClaude a09d158e16 feat: minted keys, VRF seeds, RFC 9162 proofs, history roots, tag URIs and a transparency log
Keys (spec/02 §2.4), the rule paperstack settled on:
- A work key is a Scholar-style citation key, a hyphen and the first four
  Crockford base32 characters of SHA-256("scienting-key/1:" + identity),
  where the identity is the first of doi, arxiv, isbn, pmid, pmcid and
  openalex in same_as, else the folded title, year and family name. A
  collision lengthens the later key by two characters; a key is minted
  once and never recomputed. A document key adds .si, .v<seq> or .<type>
  for versions other than the version of record.
- Every fixture key is now minted (hale2026harmonic-damb,
  okafor2025solvent-acth, writer2027fixed-ynr7.v3, tiny2026tiny-j4qm), so
  every node and record ID was re-minted and the docs rewritten to match.
  The validator checks the document key against the work key and version
  (doc.key.work) and the hash part against the identifier (doc.key.suffix).
  vectors/keys.jsonl pins the cases paperstack's tests pin.

Review:
- Spot-check seeds are ECVRF-EDWARDS25519-SHA512-TAI proofs (RFC 9381),
  implemented in tools/oracle/vrf.py in plain Python and reproduced
  against the RFC's Example 16; the sample ranks by the VRF output. The
  earlier Ed25519 seed could be ground by a dishonest checker.
- RFC 9162 inclusion and consistency proofs (crypto.inclusion_path,
  verify_inclusion, consistency_proof, verify_consistency) with
  scienting-proof/1, vectors/proofs.jsonl and two fixture records.
- document.json carries `history`, the root over the corrections, with
  the empty-tree hash for none (spec/11 §11.5, rule doc.history).
- A provisional transparency log (spec/23): log entries, signed-note
  checkpoints with key hashes as transparency logs share them, the rule
  that a logged revocation beats a self-asserted time, and vectors.
- Reserved signature schemes cose and ml-dsa-65 (spec/08 §8.4).

Identifiers and text:
- urn:scienting: becomes tag:scient.ing,2026: (RFC 4151), since the URN
  namespace was never registered.
- Unicode 16.0 is pinned for normalization and categories (spec/01 §1.4);
  the checker refuses another version.

Checking:
- check.py: keys, proofs, VRF and checkpoint vectors; the proof and
  checkpoint records; a check that every ID and hash the prose quotes
  exists in the fixtures or vectors; three more broken packages
  (history-wrong, key-suffix, key-work).
- stress.py: key, VRF, proof and checkpoint properties.
- Docs: a keys section in the concepts and FAQ, rationale entries for
  keys, VRF, proofs, tag URIs and the Unicode pin, a "Looking ahead"
  section, media-type registration and OCI distribution notes.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-10-14 23:21:57 -04:00

1824 lines
105 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#!/usr/bin/env python3
"""Build every fixture and test vector from fixtures/src and the cases below.
Non-normative tooling.
The build is deterministic: run it twice and the output is byte-identical; `tools/check.py` verifies that.
Usage: python3 tools/build.py [--out DIR] (default: the repository itself)
"""
import base64
import copy
import hashlib
import json
import os
import shutil
import struct
import sys
import zipfile
import zlib
from fractions import Fraction
ROOT = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
sys.path.insert(0, ROOT)
from tools.oracle import align as AL # noqa: E402
from tools.oracle import anchors as AN # noqa: E402
from tools.oracle import corrections as CO # noqa: E402
from tools.oracle import crypto, fingerprint as FP, grade as GR, hashing, jcs, receipts as RC, rights as RI # noqa: E402
from tools.oracle import keys as K, log as LG, targets as TG, vrf # noqa: E402
from tools.oracle.model import LEAVES, MATH_KINDS, RUN_KINDS, VERBATIM_KINDS, Doc # noqa: E402
from tools.oracle.notation import fold_math # noqa: E402
from tools.oracle.package import ZIP_EXTENSION, history_items, jsonl, line, witness_id, write, write_zip # noqa: E402
from tools.oracle.schema import Store # noqa: E402
from tools.oracle.text import letter_first, mint_node_id, norm, record_id, sha256_hex # noqa: E402
from tools.oracle.tree import Refusal, _flatten, mint_ids, validate_structure # noqa: E402
VERSION = "0.1.0"
STORE = "smithlab"
SCHEMAS = Store(os.path.join(ROOT, "schemas"))
def H(s):
return sha256_hex(s)
def secret(name):
return hashlib.sha256(("paperspec fixture key: " + name).encode()).digest()
KEYS = {
"ana": {"name": "Ana Example", "role": "owner", "purposes": ["sign"]},
"ben": {"name": "Ben Example", "role": "editor", "purposes": ["sign"]},
"ben-spot": {"name": "Ben Example", "role": "editor", "purposes": ["spot-check"]},
"cara": {"name": "Cara Example", "role": "reader", "purposes": ["sign"]},
}
TEAM_SECRET = hashlib.sha256(b"paperspec fixture team secret: smithlab").digest()
def roster():
keys = []
for kid, k in KEYS.items():
keys.append({"id": kid, "person": {"name": k["name"]}, "role": k["role"], "scheme": "ed25519",
"public": base64.b64encode(crypto.ed25519_public(secret(kid))).decode(),
"purposes": k["purposes"], "registered_at": "2026-09-01T00:00:00Z", "revoked_at": None})
return {"format": "scienting-roster/1", "store": STORE, "keys": keys}
ROSTER = roster()
ROSTER_BY_ID = {k["id"]: k for k in ROSTER["keys"]}
def png(w, h, rgb):
"""A tiny valid PNG, so a fixture can carry a real image asset."""
raw = b"".join(b"\x00" + bytes(rgb) * w for _ in range(h))
def chunk(t, d):
return struct.pack(">I", len(d)) + t + d + struct.pack(">I", zlib.crc32(t + d) & 0xFFFFFFFF)
return (b"\x89PNG\r\n\x1a\n" + chunk(b"IHDR", struct.pack(">IIBBBBB", w, h, 8, 2, 0, 0, 0))
+ chunk(b"IDAT", zlib.compress(raw, 9)) + chunk(b"IEND", b""))
# --- the shorthand of fixtures/src ------------------------------------------------
def runs_of(parts, kids):
out = []
for p in parts:
if isinstance(p, str):
out.append({"kind": "text", "text": p})
elif "sup" in p or "sub" in p or "em" in p:
st = next(k for k in ("sup", "sub", "em") if k in p)
out.append({"kind": "text", "text": p[st], "style": [st]})
elif "m" in p:
out.append({"kind": "math", "notation": "tex", "source": p["m"]})
elif "t" in p:
out.append({"kind": "math", "notation": "typst", "source": p["t"]})
elif "cite" in p:
out.append({"kind": "cite", "refs": p["cite"], "text": p["text"]})
elif "xref" in p:
out.append({"kind": "xref", "target": p["xref"], "text": p["text"]})
elif "fn" in p:
out.append({"kind": "fnmark", "footnote": p["fn"], "text": p["text"]})
elif "link" in p:
out.append({"kind": "link", "url": p["link"], "text": p["text"]})
elif "eq" in p:
e = p["eq"]
kids.append(_math_node("equation", e))
out.append({"kind": "equation", "child": len(kids) - 1})
else:
raise ValueError(p)
return out
def _math_node(kind, e):
n = {"kind": kind, "content": {"notation": "typst" if "typst" in e else "tex", "source": e.get("tex", e.get("typst"))}}
if "label" in e:
n["label"] = e["label"]
for k in ("name", "fidelity"):
if k in e:
n["_" + k if k == "name" else k] = e[k]
return n
def expand(s, page=None):
if isinstance(s, str):
s = {"s": [s]}
page = s.get("page", page)
if "s" in s:
kids = []
n = {"kind": "sentence", "content": runs_of(s["s"], kids), "children": kids}
elif "p" in s:
n = {"kind": "paragraph", "children": [expand(x, page) for x in s["p"]]}
elif "row" in s:
n = {"kind": "row", "children": [expand(x, page) for x in s["row"]]}
if "header_path" in s:
n["attrs"] = {"header_path": s["header_path"]}
elif "c" in s:
n = {"kind": "cell", "content": runs_of(s["c"], []),
"attrs": {"column": s["col"], "rowspan": s.get("rowspan", 1), "colspan": s.get("colspan", 1),
"role": s.get("role", "data")}}
n["fidelity"] = s.get("fidelity", "F1")
else:
n = {"kind": s["kind"]}
if "label" in s:
n["label"] = s["label"]
kids = []
if s["kind"] == "section":
n["attrs"] = {"level": s["level"]}
if "title" in s:
kids.append({"kind": "heading", "children": [expand({"s": [s["title"]], "fidelity": "F2"}, page)]})
if s["kind"] == "table":
n["attrs"] = {"columns": [{"id": c["name"], "header_path": c["header_path"]} for c in s["columns"]]}
if s["kind"] == "panel":
n["attrs"] = {}
if "figure_text" in s:
n["attrs"]["figure_text"] = [dict(t) for t in s["figure_text"]]
if s["kind"] in ("reference", "authors", "keywords"):
n["content"] = runs_of(s["runs"], [])
if s["kind"] in MATH_KINDS:
n = _math_node(s["kind"], s)
for k in ("csl", "resolution"):
if k in s:
n["_" + k] = s[k]
kids += [expand(x, page) for x in s.get("children", [])]
n["children"] = kids
if "name" in s:
n["_name"] = s["name"]
if "fidelity" in s and n["kind"] in LEAVES:
n["fidelity"] = s["fidelity"]
n["_page"] = page
for c in n.get("children", []):
if c.get("_page") is None:
c["_page"] = page
return n
class Names:
"""Local names (`@x`) and the IDs minted for them, per document, registered under the source's name."""
registry = {}
def __init__(self, name, key):
self.name = name
self.key = key
self.ids = {}
Names.registry[name] = self
def resolve(self, v):
if isinstance(v, str) and v.startswith("ext:"):
parts = v.split(":", 2)
other = Names.registry[parts[1]]
if len(parts) == 2: # `ext:<source>`: the document, qualified by the store
return STORE + "/" + other.key
return other.key + "#" + other.ids[parts[2]]
if isinstance(v, str) and v.startswith("@"):
return self.ids[v]
if isinstance(v, list):
return [self.resolve(x) for x in v]
return v
def resolve_refs(nodes, names):
for n in nodes:
if n["kind"] in RUN_KINDS:
out = []
for r in n["content"]:
r = dict(r)
if r["kind"] == "cite":
r["refs"] = names.resolve(r["refs"])
elif r["kind"] == "xref":
r["target"] = names.resolve(r["target"])
elif r["kind"] == "fnmark":
r["footnote"] = names.resolve(r["footnote"])
out.append(r)
n["content"] = out
a = n.get("attrs")
if a:
if "header_path" in a:
a["header_path"] = names.resolve(a["header_path"])
if "column" in a:
a["column"] = names.resolve(a["column"])
if "columns" in a:
a["columns"] = [{"id": names.resolve(c["id"]), "header_path": names.resolve(c["header_path"])}
for c in a["columns"]]
def mint_extras(key, nodes, names, used):
"""Columns and figure-text items get IDs from the same minting rule, kinds `column` and `figure-text`."""
doc = Doc(key, nodes)
seen = {}
def mint(kind, th):
occ = seen.get((kind, th), 0)
seen[(kind, th)] = occ + 1
nid = mint_node_id(key, kind, th, occ)
while nid in used:
occ += 1
nid = mint_node_id(key, kind, th, occ)
used.add(nid)
return nid
for n in nodes:
a = n.get("attrs", {})
for c in a.get("columns", []):
if c["id"].startswith("@"):
th = H(norm(" ".join(hashing.plain(doc, names.ids[h]) for h in c["header_path"])))
names.ids[c["id"]] = mint("column", th)
for t in a.get("figure_text", []):
t["id"] = mint("figure-text", H(norm(t["text"])))
if "name" in t:
names.ids[t.pop("name")] = t["id"]
def flatten_spec(spec):
flat = []
_flatten(copy.deepcopy(spec), None, 0, flat)
return flat
def establish(key, spec, names, used):
flat = flatten_spec(spec)
nodes = mint_ids(key, flat, used)
side = {}
for f, n in zip(flat, nodes):
if "_name" in f:
names.ids[f["_name"]] = n["id"]
side[n["id"]] = {k: v for k, v in f.items() if k.startswith("_")}
mint_extras(key, nodes, names, used)
resolve_refs(nodes, names)
return nodes, side
# --- geometry -----------------------------------------------------------------
def page_layout(doc, side, src, wit_id, fps_override=None):
"""Synthetic but consistent geometry: lines per page, fingerprints and boxes."""
P = src["pages"]
size = P["size"]
npages = len(P["labels"])
stamps = json.load(open(os.path.join(ROOT, "registry", "stamps", "scienting-stamps-1.json")))["rules"]
lines = {p: [] for p in range(1, npages + 1)}
boxes = {}
y = {p: 72.0 for p in lines}
for p, rh in P.get("running_head", {}).items():
lines[int(p)].append({"text": rh, "box": [72.0, 40.0, 400.0, 50.0]})
for nid in doc.preorder():
n = doc.nodes[nid]
pg = side.get(nid, {}).get("_page") or 1
if n["kind"] in ("sentence", "cell", "reference", "authors", "keywords") or (
n["kind"] == "equation" and doc.nodes[n["parent"]]["kind"] != "sentence"):
text = norm(hashing.plain(doc, nid))
lines[pg].append({"text": text})
bx = []
rest = len(text)
while True:
w = min(rest, 80)
bx.append([72.0, y[pg], round(72.0 + 5.85 * w, 2), round(y[pg] + 11.8, 2)])
y[pg] = round(y[pg] + 13.95, 2)
rest -= 80
if rest <= 0:
break
boxes[nid] = (pg, bx)
elif n["kind"] == "panel":
boxes[nid] = (pg, [[72.0, y[pg], 300.0, round(y[pg] + 150.0, 2)]])
y[pg] = round(y[pg] + 160.0, 2)
pages = []
images = {}
for nid, s in side.items():
if "_image" in s:
images.setdefault(boxes[nid][0], []).append(s["_image"])
for p in range(1, npages + 1):
ls = list(lines[p])
if "stamp" in P:
ls.append({"text": P["stamp"], "box": [8.0, 200.0, 20.0, 600.0]})
ls.append({"text": P["stamp2"], "box": [580.0, 200.0, 592.0, 600.0]})
page_in = {"size": size, "lines": ls, "images": images.get(p, [])}
fp, removed = FP.fingerprint(page_in, stamps)
rec = {"witness": wit_id, "page": p, "page_fp": fp, "size": size, "page_label": P["labels"][p - 1],
"text_layer": True}
if removed:
rec["stamps_removed"] = removed
pages.append((rec, page_in))
return pages, boxes
# --- packages -------------------------------------------------------------------
def rights_record(kind):
base = {"format": "scienting-rights/1", "at": "2026-09-15T12:00:00Z"}
if kind == "open":
base.update({"license": {"spdx": "CC-BY-4.0", "url": "https://creativecommons.org/licenses/by/4.0/"},
"basis": "open-license",
"uses": {"read": True, "tdm": True, "redistribute": True, "quote-public": True,
"accessible-copy": True, "ai": {p: True for p in RI.AI_PAIRS}},
"asserted_by": {"by": "crossref", "evidence": [
{"kind": "crossref-license", "doi": "10.5555/example.okafor.2025",
"url": "https://creativecommons.org/licenses/by/4.0/"}]},
"valid_until": None})
elif kind == "own":
base.update({"license": {"spdx": "CC-BY-4.0"}, "basis": "own-work",
"uses": {"read": True, "tdm": True, "redistribute": True, "quote-public": True,
"accessible-copy": True, "ai": {p: True for p in RI.AI_PAIRS}},
"asserted_by": {"by": "ana", "evidence": [{"kind": "statement", "note": "The team's own draft."}]},
"valid_until": None})
else:
base.update({"license": {"text": "Publisher's subscription terms (fictional)."}, "basis": "subscription",
"uses": {"read": True, "tdm": True, "redistribute": False, "quote-public": False,
"accessible-copy": "unknown",
"ai": {"internal/local": True, "internal/tenancy": "unknown",
"internal/outside-no-retention": "unknown", "internal/outside": False,
"external/local": False, "external/tenancy": False,
"external/outside-no-retention": False, "external/outside": False}},
"asserted_by": {"by": "ana", "evidence": [
{"kind": "statement", "note": "The library's subscription terms, as the team read them (fictional)."}]},
"valid_until": "2027-08-31T23:59:59Z"})
return base
def crop_sha(name):
return H("crop of " + name + " at 300 dpi (synthetic)")
def node_record(n, values):
rec = {k: n[k] for k in ("id", "kind", "parent", "ord")}
for k in ("label", "attrs", "content", "withheld", "fidelity", "prov"):
if k in n and n[k] not in (None, {}, []):
rec[k] = n[k]
rec.update(values[n["id"]])
return rec
def seal_receipts(spec, names, state_by_rev, final_state, key, wit_sha):
"""Build and sign the receipts and revocations a source lists."""
out = []
by_name = {}
final_doc = final_state.doc()
final_vals = hashing.compute(final_doc)
for r in spec:
if "revoke" in r:
rec = {"format": "scienting-revocation/1", "receipt": by_name[r["revoke"]]["id"], "reason": r["reason"],
"by": r["by"], "at": r["at"]}
rec = RC.seal(rec, secret(r["by"]), r["by"])
elif "qa_of" in r:
orig = by_name[r["qa_of"]]
seed = RC.make_seed(orig, secret(r["key"]))
sample = RC.sample(orig, seed)
hashes = dict((nid, h) for nid, h in orig["nodes"])
errs = [{"node": names.ids[e["node"]], **{k: v for k, v in e.items() if k != "node"}} for e in r["errors"]]
rec = {"format": "scienting-receipt/1", "doc": key, "rev": orig["rev"], "level": orig["level"],
"scope": "qa:" + orig["id"], "nodes": [[nid, hashes[nid]] for nid in sample],
"evidence": [{"kind": "crop", "node": nid, "sha256": crop_sha(nid)} for nid in sample],
"by": r["by"], "at": r["at"],
"qa": {"of": orig["id"], "key": r["key"], "seed": base64.b64encode(seed).decode(), "rate": "0.05",
"minimum": 10, "sample": sample, "errors": errs}}
rec = RC.seal(rec, secret(r["by"]), r["by"])
else:
rev = r.get("rev", final_state.rev)
st = state_by_rev[rev]
vals = hashing.compute(st.doc()) if rev != final_state.rev else final_vals
ids = sorted(names.ids[x] for x in r["nodes"])
scope = r["scope"]
if scope.startswith("section:@"):
scope = "section:" + names.ids[scope[8:]]
rec = {"format": "scienting-receipt/1", "doc": key, "rev": rev, "level": r["level"], "scope": scope,
"nodes": [[i, vals[i]["node_hash"]] for i in ids],
"evidence": [{"kind": "crop", "node": i, "sha256": crop_sha(i)} for i in ids]
+ [{"kind": "witness", "sha256": wit_sha}],
"by": r["by"], "at": r["at"]}
rec = RC.seal(rec, secret(r["by"]), r["by"])
by_name[r["name"]] = rec
out.append(rec)
out.sort(key=lambda x: (x["at"], x["id"]))
return out, by_name
def build_document(src, used=None, extra_files=None):
# The keys are minted, never written by hand (spec/02 §2.4): the work key from the work's identity and its
# metadata, the document key from the work key and the version.
d = src["document"]
wkey = K.work_key(d["work"].get("same_as"), d["meta"])
key = K.document_key(wkey, d["version"])
d["key"], d["work"]["key"] = key, wkey
names = Names(src.get("name", key), key)
used = used if used is not None else set()
spec = expand(src["tree"])
nodes, side = establish(key, spec, names, used)
aliases = [{"id": a, "kind": "alias", "successor": names.ids[t]}
for a, t in sorted(src.get("legacy_aliases", {}).items())]
state = CO.State(key, nodes, aliases)
states = {0: state}
records = []
for c in src.get("corrections", []):
c = copy.deepcopy(c)
crop = c.pop("crop", None)
new_name = c.pop("new_name", None)
before = c.pop("before", None)
c["evidence"] = [{"kind": "crop", "sha256": crop_sha(names.ids.get(crop, crop))}] if crop else [
{"kind": "person", "by": "ana", "note": "Read against the page."}]
c["author"] = {"key": "ana"}
for k in ("target", "parent", "place_after", "with"):
if k in c:
c[k] = names.resolve(c[k])
if "node" in c:
nspec = expand(c["node"])
flat = flatten_spec(nspec)
pre = mint_ids(key, flat, state.used_ids())
for f, n in zip(flat, pre):
if "_name" in f:
names.ids[f["_name"]] = n["id"]
c["node"] = _strip_private(nspec, names)
if before is not None:
d = state.doc()
c["offset"] = hashing.plain(d, c["target"]).index(before)
state, rec = CO.apply(state, c, store=SCHEMAS)
if new_name:
names.ids[new_name] = rec["created"][0]
records.append(rec)
states[state.rev] = state
# A corrected leaf was checked against the page image: it is F1 at most until corroborated again.
final = state
touched = set()
for rec in records:
if any(e["kind"] == "crop" for e in rec["evidence"]):
touched |= set(rec["touched"]) | set(rec.get("created", []))
for n in final.nodes:
if n["id"] in touched and n["kind"] in LEAVES:
n["fidelity"] = "F1"
for n in final.nodes:
if n["kind"] in LEAVES and "fidelity" not in n:
n["fidelity"] = "F1"
return key, names, side, final, states, records
def _strip_private(spec, names):
out = {k: v for k, v in spec.items() if not k.startswith("_") and k != "children"}
if out["kind"] == "row" and "attrs" in out:
out["attrs"] = {"header_path": names.resolve(out["attrs"]["header_path"])}
if out["kind"] == "cell":
out["attrs"] = dict(out["attrs"], column=names.resolve(out["attrs"]["column"]))
if spec.get("children"):
out["children"] = [_strip_private(c, names) for c in spec["children"]]
return out
def package_files(src, key, names, side, final, records, receipts=None, roster_rec=None, witnesses=None,
pages=None, boxes=None, extra=None, absent=()):
doc = final.doc()
vals = hashing.compute(doc)
nodes = [node_record(doc.nodes[nid], vals) for nid in doc.preorder()]
files = {}
files["nodes.jsonl"] = jsonl(nodes)
if pages:
files["pages.jsonl"] = jsonl([p for p, _ in sorted(pages, key=lambda x: (x[0]["witness"], x[0]["page"]))])
locs = []
fps = {p["page"]: p for p, _ in pages}
pos = {nid: i for i, nid in enumerate(doc.preorder())}
for nid, (pg, bx) in boxes.items():
if nid in doc.nodes:
locs.append({"node": nid, "page_fp": fps[pg]["page_fp"], "page": pg,
"page_label": fps[pg]["page_label"], "boxes": bx})
locs.sort(key=lambda l: (pos[l["node"]], l["page"], l["page_fp"]))
if locs:
files["locations.jsonl"] = jsonl(locs)
refs = []
for nid in doc.preorder():
s = side.get(nid, {})
if doc.nodes[nid]["kind"] == "reference":
r = {"node": nid, "label": doc.nodes[nid].get("label")}
if "_csl" in s:
r["csl"] = s["_csl"]
res = s.get("_resolution", {"type": "none"})
r["resolution"] = {k: names.resolve(v) for k, v in res.items()}
refs.append({k: v for k, v in r.items() if v is not None})
if refs:
files["references.jsonl"] = jsonl(refs)
if records:
files["corrections.jsonl"] = jsonl(records)
ids = sorted(final.ids, key=lambda e: (0 if e["kind"] == "alias" else 1, e.get("rev", 0), e["id"]))
if ids:
files["ids.jsonl"] = jsonl(ids)
if receipts:
files["receipts.jsonl"] = jsonl(receipts)
if roster_rec:
files["roster.json"] = (jcs.dumps(roster_rec) + "\n").encode()
files["witnesses.json"] = (jcs.dumps({"witnesses": witnesses}) + "\n").encode()
files["provenance.json"] = (jcs.dumps({
"tools": [{"name": "paperspec tools/build.py", "version": VERSION}],
"stages": [{"stage": "hand", "processor": "local", "tool": "paperspec tools/build.py"}],
"note": "Built by hand for the conformance suite; every reading is synthetic."}) + "\n").encode()
levels = RC.effective_levels(nodes, vals, receipts or [], ROSTER_BY_ID if roster_rec else {})
d = dict(src["document"])
d = {"format": "scienting-doc/1", **d}
d.update({"hashing": {"scheme": "plain"}, "root": vals[doc.root]["node_hash"], "rev": final.rev,
"history": crypto.history_root(history_items(records or [])),
"counts": {"nodes": len(nodes), "leaves": sum(1 for n in nodes if n["kind"] in LEAVES)},
"fidelity": RC.distribution(levels)})
d["meta_hash"] = hashing.meta_hash(d)
files["document.json"] = (jcs.dumps(d) + "\n").encode()
if extra:
files.update(extra)
return files, nodes, vals, d
def build_hale(out):
src = json.load(open(os.path.join(ROOT, "fixtures", "src", "hale2026harmonic.json")))
src["name"] = "hale2026harmonic"
image = png(4, 3, (40, 90, 160))
image_sha = sha256_hex(image)
key, names, side, final, states, records = build_document(src)
side[names.ids["@pa"]]["_image"] = image_sha
doc = final.doc()
pa = doc.nodes[names.ids["@pa"]]
pa["attrs"]["image"] = {"sha256": image_sha, "media_type": "image/png", "native": True}
pa["attrs"]["crop"] = {"sha256": crop_sha("panel a"), "media_type": "image/png", "dpi": 300}
P = src["pages"]
wsha = H(P["witness"]["sha_seed"])
wid = witness_id(wsha)
for n in final.nodes:
if n["kind"] in LEAVES:
n["prov"] = {"witnesses": [wid], "backend": "docling (illustrative)"}
if n.get("fidelity") == "F2":
n["prov"]["corroborated_by"] = [wid]
pages, boxes = page_layout(final.doc(), side, src, wid)
p2 = [p for p, _ in pages if p["page"] == 2][0]
for t in pa["attrs"]["figure_text"]:
t["page_fp"] = p2["page_fp"]
t["boxes"] = [[90.0, 300.0, 140.0, 310.0]]
witnesses = [{"id": wid, "sha256": wsha, "size": P["witness"]["size"], "kind": "pdf", "media_type": "application/pdf",
"pages": len(P["labels"]), "text_layer": True, "version_label": "version of record",
"origin": {"source": "publisher", "doi": "10.5555/example.hale.2026", "retrieved_at": "2026-10-01T08:00:00Z"},
"rights": rights_record("subscription")}]
receipts, by_name = seal_receipts(src["receipts"], names, states, final, key, wsha)
# The dataset reported from Table 1.
final_doc = final.doc()
vals = hashing.compute(final_doc)
rows = [("ct-ct", "CT–CT", "@cc"), ("ct-hc", "CT–HC", "@ch2"), ("ow-hw", "OW–HW", "@ow")]
csv = ["key,bond,set,k_r,r_eq"]
anchors = []
for rk, bond, pre in rows:
for st, (kc, rc) in (("A", ("_ak", "_ar")), ("B", ("_bk", "_br"))):
kid, rid = names.ids[pre + kc], names.ids[pre + rc]
kv, rv = norm(hashing.plain(final_doc, kid)), norm(hashing.plain(final_doc, rid))
row_key = rk + "-" + st.lower()
csv.append("%s,%s,%s,%s,%s" % (row_key, bond, st, kv, rv))
for field, nid in (("k_r", kid), ("r_eq", rid)):
anchors.append({"resource": "bond-parameters", "row": row_key, "field": field,
"anchor": AN.make_anchor(final_doc, vals, nid, final.rev, store=STORE)})
ds_id = "ds-hale-bond-params"
datapackage = {"format": "scienting-dataset/1", "id": ds_id, "name": "hale-bond-params",
"title": "Harmonic bond parameters reported in Table 1",
"resources": [{"name": "bond-parameters", "path": "data.csv", "format": "csv", "encoding": "utf-8",
"schema": {"fields": [
{"name": "key", "type": "string"}, {"name": "bond", "type": "string"},
{"name": "set", "type": "string"},
{"name": "k_r", "type": "number", "unit": "kcal / mol / angstrom ** 2"},
{"name": "r_eq", "type": "number", "unit": "angstrom",
"uncertainty": {"kind": "unknown"}}],
"primaryKey": "key"},
"origin": {"kind": "reported", "anchors": "anchors.jsonl"}}]}
eq = names.ids["@eq1"]
mathml = ('<math display="block"><msub><mi>E</mi><mtext>bond</mtext></msub><mo>=</mo><mfrac><mn>1</mn><mn>2'
'</mn></mfrac><msub><mi>K</mi><mi>r</mi></msub><msup><mrow><mo>(</mo><mi>r</mi><mo>−</mo><msub><mi>r'
'</mi><mtext>eq</mtext></msub><mo>)</mo></mrow><mn>2</mn></msup></math>')
extra = {
"assets/%s.png" % image_sha: image,
"datasets/%s/datapackage.json" % ds_id: (jcs.dumps(datapackage) + "\n").encode(),
"datasets/%s/data.csv" % ds_id: ("\n".join(csv) + "\n").encode(),
"datasets/%s/anchors.jsonl" % ds_id: jsonl(anchors),
"derived/mathml.jsonl": jsonl([{"node": eq, "run": None, "tool": {"name": "hand", "version": "0"},
"mathml": mathml}]),
}
absent = [{"path": "assets/%s.png" % crop_sha("panel a"), "sha256": crop_sha("panel a"), "size": 18213,
"present": False}]
files, nodes, vals, d = package_files(src, key, names, side, final, records, receipts, ROSTER, witnesses, pages,
boxes, extra, absent)
write(os.path.join(out, "fixtures", "packages", key), files, absent)
return {"src": src, "key": key, "names": names, "states": states, "final": final, "records": records,
"receipts": receipts, "by_name": by_name, "pages": pages, "files": files, "nodes": nodes, "values": vals,
"document": d, "witness": witnesses[0], "absent": absent}
def build_checks_only(out, hale):
"""The same document exported to another machine of the team: text withheld, hashes keyed."""
final = hale["final"]
doc = final.doc()
k = crypto.team_key(TEAM_SECRET)
keyed = hashing.compute(doc, hashing.Hasher(k))
nodes = []
for nid in doc.preorder():
n = copy.deepcopy(doc.nodes[nid])
if n["kind"] in LEAVES:
n.pop("content", None)
n["withheld"] = True
if n["kind"] == "panel":
n["attrs"].pop("figure_text", None)
nodes.append(node_record(n, keyed))
f = dict(hale["files"])
for p in ("corrections.jsonl", "receipts.jsonl", "roster.json", "derived/mathml.jsonl"):
f.pop(p, None)
for p in [p for p in f if p.startswith("datasets/")]:
f.pop(p)
f["nodes.jsonl"] = jsonl(nodes)
refs = [{"node": r["node"], "label": r["label"], "resolution": r["resolution"]}
for r in map(json.loads, f["references.jsonl"].decode().splitlines())]
f["references.jsonl"] = jsonl(refs)
d = dict(hale["document"])
d.update({"policy": "checks-only", "hashing": {"scheme": "hmac-team", "key_id": crypto.key_id(k)},
"root": keyed[doc.root]["node_hash"], "history": crypto.EMPTY_ROOT})
d["meta_hash"] = hashing.meta_hash(d, hashing.Hasher(k))
f["document.json"] = (jcs.dumps(d) + "\n").encode()
write(os.path.join(out, "fixtures", "packages", hale["key"] + ".checks-only"), f, hale["absent"])
def build_simple(out, name, used=None):
src = json.load(open(os.path.join(ROOT, "fixtures", "src", name + ".json")))
src["name"] = name
key, names, side, final, states, records = build_document(src)
witnesses = []
pages = boxes = None
if "pages" in src:
P = src["pages"]
wsha = H(P["witness"]["sha_seed"])
wid = witness_id(wsha)
jsha = H(P["jats"]["sha_seed"])
jid = witness_id(jsha)
for n in final.nodes:
if n["kind"] in LEAVES:
n["prov"] = {"witnesses": [jid, wid], "backend": "jats reader (illustrative)", "corroborated_by": [wid]}
pages, boxes = page_layout(final.doc(), side, src, wid)
witnesses = [
{"id": jid, "sha256": jsha, "size": P["jats"]["size"], "kind": "jats", "media_type": "application/xml",
"version_label": "version of record",
"origin": {"source": "repository", "doi": "10.5555/example.okafor.2025"}, "rights": rights_record("open")},
{"id": wid, "sha256": wsha, "size": P["witness"]["size"], "kind": "pdf", "media_type": "application/pdf",
"pages": len(P["labels"]), "text_layer": True, "version_label": "version of record",
"origin": {"source": "publisher", "doi": "10.5555/example.okafor.2025"}, "rights": rights_record("open")}]
else:
T = src["typst"]
tsha = H(T["sha_seed"])
tid = witness_id(tsha)
for n in final.nodes:
if n["kind"] in LEAVES:
n["prov"] = {"witnesses": [tid], "backend": "typst reader (illustrative)"}
witnesses = [{"id": tid, "sha256": tsha, "size": T["size"], "kind": "typst", "media_type": "application/zip",
"version_label": "draft 3", "origin": {"source": "team"}, "rights": rights_record("own")}]
files, nodes, vals, d = package_files(src, key, names, side, final, records, None, None, witnesses, pages, boxes)
write(os.path.join(out, "fixtures", "packages", key), files)
return {"key": key, "names": names, "final": final, "files": files, "values": vals, "src": src}
# --- the tiny document used for corrections vectors and invalid packages -----------------
TINY = {"name": "tiny2026example",
"document": {"kind": "article", "work": {"same_as": ["doi:10.5555/example.tiny.2026"]},
"version": {"type": "version-of-record"}, "relations": [],
"meta": {"title": "A tiny example", "author": [{"family": "Tiny", "given": "T."}],
"issued": {"date-parts": [[2026]]}},
"language": "en", "license": {"spdx": "CC0-1.0"}, "policy": "full"},
"tree": {"kind": "document", "children": [
{"kind": "section", "name": "@sec", "label": "1", "level": 1, "title": "Results", "children": [
{"name": "@p1", "p": [
{"name": "@a", "s": ["The value is 42.0 at 300 K."], "fidelity": "F2"},
{"name": "@b", "s": ["See ", {"xref": "@t", "text": "Table 1"}, " and the note.", {"cite": ["@r"], "text": "1"}],
"fidelity": "F2"}]},
{"name": "@p2", "p": [{"name": "@c", "s": ["A second paragraph."], "fidelity": "F2"},
{"name": "@d", "s": ["It ends here."], "fidelity": "F2"}]},
{"kind": "table", "name": "@t", "label": "Table 1",
"columns": [{"name": "@cx", "header_path": ["@hx"]}, {"name": "@cy", "header_path": ["@hy"]}],
"children": [
{"kind": "table-head", "children": [{"row": [
{"name": "@hx", "c": ["x"], "role": "column-header", "col": "@cx"},
{"name": "@hy", "c": ["y"], "role": "column-header", "col": "@cy"}]}]},
{"kind": "table-body", "name": "@tb", "children": [
{"name": "@row1", "header_path": ["@x1"], "row": [
{"name": "@x1", "c": ["1"], "role": "row-header", "col": "@cx"},
{"name": "@y1", "c": ["2.5"], "col": "@cy"}]}]}]}]},
{"kind": "back", "children": [{"kind": "references", "children": [
{"kind": "reference", "name": "@r", "label": "1", "runs": ["1. An Example, 2020."],
"resolution": {"type": "none"}, "fidelity": "F2"}]}]}]}}
def tiny_package(out_dir, receipts=True):
src = copy.deepcopy(TINY)
key, names, side, final, states, records = build_document(src)
wsha = H("tiny2026example: a file (synthetic)")
wit = [{"id": witness_id(wsha), "sha256": wsha, "size": 1000, "kind": "pdf", "media_type": "application/pdf",
"pages": 1, "origin": {"source": "author"}, "rights": rights_record("own")}]
recs = None
if receipts:
recs, _ = seal_receipts([{"name": "t1", "by": "ana", "level": "F3", "scope": "all", "at": "2026-10-11T12:00:00Z",
"nodes": ["@a", "@b", "@c", "@d", "@x1", "@y1", "@hx", "@hy", "@r"]}],
names, states, final, key, wsha)
files, nodes, vals, d = package_files(src, key, names, side, final, records, recs, ROSTER if receipts else None,
wit)
if out_dir:
write(out_dir, files)
return {"key": key, "names": names, "final": final, "files": files, "values": vals}
# --- vectors --------------------------------------------------------------------
def wv(out, name, cases):
path = os.path.join(out, "vectors", name)
with open(path, "wb") as f:
f.write(jsonl(cases))
def sentence_doc(runs, key="vectors"):
"""A one-sentence document, so projections of runs can be computed with the node functions."""
nodes = [{"id": "aaaaaaa", "kind": "document", "parent": None, "ord": 0},
{"id": "bbbbbbb", "kind": "section", "parent": "aaaaaaa", "ord": 0, "attrs": {"level": 1}},
{"id": "ccccccc", "kind": "paragraph", "parent": "bbbbbbb", "ord": 0},
{"id": "ddddddd", "kind": "sentence", "parent": "ccccccc", "ord": 0, "content": runs}]
return Doc(key, nodes), "ddddddd"
def projection_case(cid, runs, note=None):
doc, s = sentence_doc(runs)
strict, cx = hashing.strict(doc, s)
lossy = hashing.lossy_from_strict(strict)
plain = hashing.plain(doc, s)
c = {"id": cid, "runs": runs, "plain": plain, "normalized": norm(plain), "strict": strict, "lossy": lossy,
"complex": cx, "text_hash": H(norm(plain)), "match_hash": H(strict), "align_key": H(lossy)}
if note:
c["note"] = note
return c
T = lambda t, **kw: dict({"kind": "text", "text": t}, **({"style": kw["style"]} if "style" in kw else {})) # noqa: E731
M = lambda s, n="tex": {"kind": "math", "notation": n, "source": s} # noqa: E731
def parse_doubles(text):
"""Parse JSON as an ECMAScript engine does: every number is an IEEE 754 double."""
return json.loads(text, parse_int=lambda x: int(x) if abs(int(x)) <= 2**53 else float(x))
def vectors(out, hale, tiny):
# Canonical JSON.
cj = [
("sort-utf16", '{"\\u20ac": 1, "\\ud83d\\ude00": 2, "a": 3, "\\ufb01": 4}'),
("numbers", '[0, -0.0, 1.0, 100.0, 72.10, 1E21, 1e-7, 0.000001, 123456789012345678901, -2.5e-5, 4.50e2]'),
("escapes", '{"s": "tab\\tnl\\ncr\\rq\\"bs\\\\ctl\\u0001del\\u007fnbsp\\u00a0slash\\/"}'),
("nested", '{ "b" : [ true , false , null ] , "a" : { "z" : "" , "y" : [ ] } }'),
("strings-stay-unnormalized", '{"cafe\\u0301": "e\\u0301", "caf\\u00e9": "\\u00e9"}'),
]
wv(out, "canonical-json.jsonl", [{"id": i, "input_json": t, "output": jcs.dumps(parse_doubles(t))} for i, t in cj])
# Normalization for hashing.
ns = [("nfc", "cafe\u0301"), ("collapse", "a \t\n b"), ("trim", "\u00a0 a b \u3000"),
("angstrom-sign", "1.5 \u212b"), ("nbsp-thin", "300\u2009K and 10\u00a0mol"),
("zero-width-kept", "a\u200bb"), ("ligature-kept", "\ufb01t"), ("micro-kept", "5 \u00b5m"),
("line-separator", "one\u2028two")]
wv(out, "normalize.jsonl", [{"id": i, "input": s, "output": norm(s), "sha256": H(norm(s))} for i, s in ns])
# IDs.
idc = []
for doc_key, kind, th, occ in [("cornell1995second", "sentence", H("A sentence."), 0),
("cornell1995second", "sentence", H("A sentence."), 1),
("cornell1995second.si", "sentence", H("A sentence."), 0),
(hale["key"], "cell", H("310.0"), 0),
(hale["key"], "column", H("Set A $K_r$ (kcal mol⁻¹ Å⁻²)"), 0)]:
payload = jcs.dumps(["scienting-id/1", doc_key, kind, th, occ])
idc.append({"id": "mint-%s-%s-%d" % (doc_key, kind, occ), "doc": doc_key, "kind": kind, "text_hash": th,
"occurrence": occ, "payload": payload, "sha256": H(payload),
"node_id": mint_node_id(doc_key, kind, th, occ)})
for hx in ("00" * 32, "ff" * 32, H("x")):
idc.append({"id": "letter-first-" + hx[:8], "digest": hx, "node_id": letter_first(bytes.fromhex(hx), 7),
"record_id": record_id(hx)})
wv(out, "ids.jsonl", idc)
# Notation.
notes = [("tex", "H_2O"), ("tex", "_2"), ("tex", "\\mathrm{H}_{2}\\mathrm{O}"), ("typst", "H_2 O"),
("typst", "upright(H)_2 upright(O)"), ("tex", "\\mathrm{cm}^{-1}"), ("typst", "\"cm\"^(-1)"),
("tex", "10^{-3}"), ("typst", "10^(-3)"), ("tex", "K_r"), ("tex", "K_{\\mathrm{r}}"), ("typst", "K_r"),
("tex", "\\alpha_i"), ("tex", "\\alpha_{i}"), ("tex", "x'_1"), ("tex", "x^{\\prime}"),
("tex", "T = 300\\,\\mathrm{K}"), ("tex", "-1.5"), ("tex", "\\epsilon"), ("typst", "epsilon.alt"),
("tex", "a \\cdot b"), ("typst", "a dot b"), ("tex", "a \\times b \\pm c"), ("tex", "\\left(x\\right)^2"),
("typst", "(a+b)^2"), ("tex", "\\frac{a}{b}"), ("typst", "a/b"), ("tex", "\\sqrt{2}"),
("tex", "\\mathbf{F}"), ("tex", "{ab}_2"), ("tex", "x_1_2"), ("tex", "\\Delta G^\\circ"),
("tex", "E_{\\mathrm{bond}} = \\frac{1}{2} K_r (r - r_{\\mathrm{eq}})^2"), ("typst", "E = K_r (r - r_\"eq\")^2"),
# A script after an empty group applies to the atom before it, or to an empty base (the isotope idiom).
("tex", "{}^{14}C"), ("tex", "x{}^2"), ("tex", "{}_2"), ("tex", "\\left)x\\right("),
# Letters are the Latin and Greek blocks: a fullwidth or modifier letter is complex, a Coptic letter too.
("tex", "A"), ("tex", "ʰ"), ("tex", "Ϣ"), ("tex", "ἀω")]
wv(out, "notation.jsonl", [{"id": "%s:%s" % (n, s), "notation": n, "source": s, "canonical": fold_math(n, s)}
for n, s in notes])
# Projections.
pc = [
projection_case("h2o-math", [T("The solvent is "), M("H_2O"), T(".")]),
projection_case("h2o-split-math", [T("The solvent is H"), M("_2"), T("O.")]),
projection_case("h2o-mathrm", [T("The solvent is "), M("\\mathrm{H}_{2}\\mathrm{O}"), T(".")]),
projection_case("h2o-sub", [T("The solvent is H"), T("2", style=["sub"]), T("O.")]),
projection_case("h2o-unicode", [T("The solvent is H\u2082O.")]),
projection_case("h2o-typst", [T("The solvent is "), M("H_2 O", "typst"), T(".")]),
projection_case("wavenumber-unicode", [T("A band near 1595 cm\u207b\u00b9.")]),
projection_case("wavenumber-sup", [T("A band near 1595 cm"), T("-1", style=["sup"]), T(".")]),
projection_case("wavenumber-math", [T("A band near 1595 "), M("\\mathrm{cm}^{-1}"), T(".")]),
projection_case("wavenumber-typst", [T("A band near 1595 "), M("\"cm\"^(-1)", "typst"), T(".")]),
projection_case("milli-unicode", [T("Values below 10\u207b\u00b3 were dropped.")]),
projection_case("milli-sup", [T("Values below 10"), T("\u22123", style=["sup"]), T(" were dropped.")]),
projection_case("milli-math", [T("Values below "), M("10^{-3}"), T(" were dropped.")]),
projection_case("kr-math", [T("The constant "), M("K_r"), T(" is large.")]),
projection_case("kr-mathrm", [T("The constant "), M("K_{\\mathrm{r}}"), T(" is large.")]),
projection_case("kr-sub", [T("The constant K"), T("r", style=["sub"]), T(" is large.")]),
projection_case("kr-unicode", [T("The constant K\u1d63 is large.")]),
projection_case("ligature", [T("The \ufb01t is good.")]),
projection_case("no-ligature", [T("The fit is good.")]),
projection_case("micro-sign", [T("A 5 \u00b5m film.")]),
projection_case("greek-mu", [T("A 5 \u03bcm film.")]),
projection_case("lost-superscript", [T("A rate of 103 per second.")], "OCR lost the superscript."),
projection_case("superscript", [T("A rate of 10\u00b3 per second.")]),
projection_case("cite-mark", [T("The energy"), {"kind": "cite", "refs": [], "text": "3"}, T(" is low.")]),
projection_case("cite-as-text", [T("The energy3 is low.")]),
projection_case("complex", [T("The energy is "), M("\\frac{1}{2} k x^2"), T(" exactly.")]),
projection_case("hyphen-before-digit", [T("pages 10-20 and CT\u2013CT with 6-31G*")]),
projection_case("en-dash-before-digit", [T("pages 10\u201320 and CT-CT with 6\u201331G*")]),
projection_case("astral", [T("Let "), M("\U0001d465"), T(" vary.")]),
]
groups = [
{"id": "h2o", "same": "match_hash",
"cases": ["h2o-math", "h2o-split-math", "h2o-mathrm", "h2o-sub", "h2o-unicode", "h2o-typst"]},
{"id": "wavenumber", "same": "match_hash",
"cases": ["wavenumber-unicode", "wavenumber-sup", "wavenumber-math", "wavenumber-typst"]},
{"id": "milli", "same": "match_hash", "cases": ["milli-unicode", "milli-sup", "milli-math"]},
{"id": "kr", "same": "match_hash", "cases": ["kr-math", "kr-mathrm", "kr-sub", "kr-unicode"]},
{"id": "ligature", "same": "match_hash", "cases": ["ligature", "no-ligature"]},
{"id": "micro", "same": "match_hash", "cases": ["micro-sign", "greek-mu"]},
{"id": "103-align", "same": "align_key", "cases": ["lost-superscript", "superscript"]},
{"id": "103-match", "differ": "match_hash", "cases": ["lost-superscript", "superscript"]},
{"id": "cite-structure", "differ": "match_hash", "cases": ["cite-mark", "cite-as-text"]},
{"id": "dashes-align", "same": "align_key", "cases": ["hyphen-before-digit", "en-dash-before-digit"]},
{"id": "dashes-text", "differ": "text_hash", "cases": ["hyphen-before-digit", "en-dash-before-digit"]},
]
byid = {c["id"]: c for c in pc}
for g in groups:
vals = {byid[c][g.get("same") or g.get("differ")] for c in g["cases"]}
assert (len(vals) == 1) if "same" in g else (len(vals) == len(g["cases"])), g
wv(out, "projections.jsonl", pc)
wv(out, "projection-groups.jsonl", groups)
# Offsets.
off = []
for cid, runs, span in [("bmp", [T("A sentence about H"), M("_2"), T("O.")], [18, 24]),
("astral", [T("Let "), M("\U0001d465"), T(" vary with "), M("\U0001d466"), T(".")], [4, 7])]:
doc, s = sentence_doc(runs)
p = hashing.plain(doc, s)
from tools.oracle.text import cp_to_utf16
off.append({"id": cid, "runs": runs, "plain": p, "span": span, "exact": p[span[0]:span[1]],
"utf16_span": [cp_to_utf16(p, span[0]), cp_to_utf16(p, span[1])], "code_points": len(p),
"utf16_units": cp_to_utf16(p, len(p))})
wv(out, "offsets.jsonl", off)
# Five values on a small tree: the tiny document.
tnodes = [{k: v for k, v in n.items() if k not in ("text_hash", "match_hash", "align_key", "own_hash", "node_hash")}
for n in map(json.loads, tiny["files"]["nodes.jsonl"].decode().splitlines())]
tdoc = Doc(tiny["key"], tnodes)
tv = hashing.compute(tdoc)
own = {nid: hashing.own_content(tdoc, nid, lambda x: tv[x]["text_hash"]) for nid in tdoc.order}
wv(out, "hashes.jsonl", [{"id": "tiny2026example", "doc": tiny["key"], "nodes": tnodes,
"own_content": own, "values": tv}])
# Addresses. The example keys are real work keys: Cornell et al. (1995) by its DOI, a fictional preprint.
cornell = "cornell1995second-" + K.suffix("doi:10.1021/ja00124a002")
dong = "dong2026machine-" + K.suffix("doi:10.5555/example.dong.2026")
ad = [("plain", cornell + "#k3m9q2a"), ("pinned", cornell + "#k3m9q2a@4f1a0c"),
("span", cornell + "#k3m9q2a@4f1a0c[12:57]"), ("store", "smithlab/" + cornell + "#k3m9q2a"),
("span-no-pin", dong + ".v2#k3m9q2a[0:0]"), ("si", cornell + ".si#m2x8p4d"),
("alias", cornell + "#b-p03-text-004"), ("bad-reversed", cornell + "#k3m9q2a[57:12]"),
("bad-pin", cornell + "#k3m9q2a@4F1A0C"), ("bad-leading-zero", "k-0000#k3m9q2a[01:2]"),
("bad-no-node", cornell + "#"), ("bad-upper-key", "Cornell1995-k3f9#k3m9q2a"),
("bad-two-stores", "a/b/c-0000#k3m9q2a"), ("bad-pin-length", "k-0000#k3m9q2a@4f1a"),
("bad-no-hash", "cornell1995second#k3m9q2a"), ("bad-hash-alphabet", "cornell1995second-k3fi#k3m9q2a"),
("bad-short-hash", "cornell1995second-k3f#k3m9q2a")]
wv(out, "addresses.jsonl", [{"id": i, "address": a, "parsed": AN.parse_address(a)} for i, a in ad])
# Keyed hashes.
k = crypto.team_key(TEAM_SECRET)
fps = [p["page_fp"] for p, _ in hale["pages"]]
pk = crypto.page_key(fps, "layer:smithlab/kp4m2x7q")
text = "310.0"
keyed = [
{"id": "team-key", "secret": TEAM_SECRET.hex(), "key": k.hex(), "key_id": crypto.key_id(k)},
{"id": "team-hmac-text", "key": k.hex(), "text": text, "text_hash_plain": H(text),
"text_hash_keyed": hashing.Hasher(k)(text)},
{"id": "page-hashes", "page_fps": fps, "page_hashes": [crypto.page_hash(f) for f in fps]},
{"id": "page-key", "page_fps": fps, "context": "layer:smithlab/kp4m2x7q", "key": pk.hex(),
"quote": "310.0", "quote_mac": hashing.Hasher(pk)(norm(text))},
{"id": "non-holder", "note": "Without the pages, a guess hashes to the plain value, which the export never "
"carries; with one page changed, the key differs.",
"guess": text, "guess_sha256": H(text),
"wrong_page_fps": [fps[0], H("another page")],
"wrong_key": crypto.page_key([fps[0], H("another page")], "layer:smithlab/kp4m2x7q").hex(),
"quote_mac": hashing.Hasher(pk)(norm(text))},
]
assert keyed[4]["wrong_key"] != pk.hex() and keyed[4]["guess_sha256"] != keyed[4]["quote_mac"]
wv(out, "keyed.jsonl", keyed)
# Fingerprints: two downloads of one page, stamped differently.
stamps = json.load(open(os.path.join(ROOT, "registry", "stamps", "scienting-stamps-1.json")))["rules"]
_, p1 = hale["pages"][0]
body = [l for l in p1["lines"] if "box" not in l]
acs1 = dict(p1)
acs2 = dict(p1, lines=body + [
{"text": "Downloaded via EXAMPLE PHARMA INC on March 2, 2027 at 09:14:55 (UTC).", "box": [8.0, 210.0, 20.0, 590.0]},
{"text": "See https://pubs.acs.org/sharingguidelines for options on how to legitimately share published articles.",
"box": [580.0, 200.0, 592.0, 600.0]}])
wiley = {"size": [595.276, 841.89], "images": [H("figure 2 image stream (synthetic)")], "lines": [
{"text": "Results"}, {"text": "The fitted slope is 0.42 \u00b1 0.03 per kelvin."},
{"text": "Downloaded from https://onlinelibrary.wiley.com/doi/10.5555/example.wiley by Example University, "
"Wiley Online Library on [11/10/2026]. See the Terms and Conditions (https://onlinelibrary.wiley.com/"
"terms-and-conditions) on Wiley Online Library for rules of use.", "box": [580.0, 100.0, 592.0, 740.0]}]}
wiley2 = dict(wiley, lines=wiley["lines"][:2] + [
{"text": "Downloaded from https://onlinelibrary.wiley.com/doi/10.5555/example.wiley by Example Pharma Inc, "
"Wiley Online Library on [02/03/2027].", "box": [580.0, 100.0, 592.0, 740.0]},
{"text": "See the Terms and Conditions (https://onlinelibrary.wiley.com/terms-and-conditions) on Wiley Online "
"Library for rules of use.", "box": [580.0, 60.0, 592.0, 99.0]}])
changed = dict(acs1, lines=[dict(l, text=l["text"].replace("1595", "1594")) for l in acs1["lines"]])
scan = {"size": [612.4, 791.6], "lines": [], "images": [H("scanned page image (synthetic)")]}
fpc = []
for cid, pg in [("acs-download-1", acs1), ("acs-download-2", acs2), ("acs-changed-text", changed),
("wiley-download-1", wiley), ("wiley-download-2", wiley2), ("scanned", scan)]:
fp, removed = FP.fingerprint(pg, stamps)
kept = [l["text"] for l in pg["lines"] if not FP.is_stamp(l, pg["size"], stamps)]
fpc.append({"id": cid, "page": pg, "rules": "scienting-stamps-1", "removed": removed,
"text": FP.fp_text(" ".join(kept)), "page_fp": fp})
assert fpc[0]["page_fp"] == fpc[1]["page_fp"] != fpc[2]["page_fp"] and fpc[3]["page_fp"] == fpc[4]["page_fp"]
wv(out, "fingerprints.jsonl", fpc)
# Merkle roots.
mk = []
for n in (1, 2, 3, 4, 5, 7):
items = [["node", mint_node_id("m", "sentence", H(str(i)), 0), H("h%d" % i)] for i in range(n)]
mk.append({"id": "items-%d" % n, "items": items, "root": crypto.merkle_root(items)})
wv(out, "merkle.jsonl", mk)
# Signatures.
r1 = hale["by_name"]["r1"]
sk = bytes.fromhex("9d61b19deffd5a60ba844af492ec2cc44449c5697b326919703bac031cae7f60")
wv(out, "signatures.jsonl", [
{"id": "rfc8032-test-1", "secret": sk.hex(), "public": crypto.ed25519_public(sk).hex(), "message": "",
"signature": crypto.ed25519_sign(sk, b"").hex()},
{"id": "receipt-r1", "receipt": r1, "record_hash": RC.record_hash(r1),
"payload": RC.signing_payload(r1).decode(), "public": ROSTER_BY_ID["ana"]["public"],
"record_id": r1["id"]}])
# Spot-checks.
q1 = hale["by_name"]["q1"]
wv(out, "spotcheck.jsonl", [{"id": "q1", "receipt": r1, "seed_payload": RC.seed_payload(r1).decode(),
"checker_public": ROSTER_BY_ID["ben-spot"]["public"], "seed": q1["qa"]["seed"],
"beta": vrf.proof_to_hash(base64.b64decode(q1["qa"]["seed"])).hex(),
"n": len(r1["nodes"]), "rate": "0.05", "minimum": 10,
"size": RC.sample_size(len(r1["nodes"])), "sample": q1["qa"]["sample"]}]
+ [{"id": "size-%d" % n, "n": n, "rate": "0.05", "minimum": 10, "size": RC.sample_size(n)}
for n in (3, 10, 199, 200, 201, 3000)])
# Bounds.
wv(out, "bounds.jsonl", [{"id": "k%d-n%d" % (kk, nn), "errors": kk, "sampled": nn, "confidence": "0.95",
"bound": RC.bound_string(kk, nn), "approx": round(RC.upper_bound(kk, nn), 9)}
for kk, nn in [(0, 10), (0, 300), (1, 10), (1, 300), (2, 50), (5, 100), (0, 1), (10, 10)]])
# Keys (spec/02 §2.4): Scholar parts, identities and suffixes, including the cases paperstack pins.
attn = {"title": "Attention is all you need", "author": [{"family": "Vaswani", "given": "Ashish"}],
"issued": {"date-parts": [[2017]]}}
kc = []
for cid, same_as, meta in [
("doi", ["doi:10.48550/arXiv.1706.03762"], attn),
("openalex", ["openalex:W2963403868"], attn),
("doi-before-arxiv", ["arxiv:1706.03762", "doi:10.48550/arXiv.1706.03762"], attn),
("arxiv-only", ["arxiv:1706.03762"], attn),
("url-is-not-an-identity", ["url:https://example.org/paper"], attn),
("meta", [], {"title": "On the Origin of Species", "author": [{"family": "Darwin", "given": "Charles"}],
"issued": {"date-parts": [[1859]]}}),
("doi-case", ["doi:10.1109/CVPR.2016.90"], {"title": "Deep Residual Learning for Image Recognition",
"author": [{"family": "He", "given": "Kaiming"}],
"issued": {"date-parts": [[2016]]}}),
("accents", [], {"title": "A new method", "author": [{"literal": "M\u00fcller, J\u00fcrgen"}],
"issued": {"date-parts": [[2001]]}}),
("apostrophe", [], {"title": "Don't stop", "author": [{"literal": "Conan O'Brien"}],
"issued": {"date-parts": [[2020]]}}),
("name-suffix", [], {"title": "3D printing of bone", "author": [{"literal": "A. Smith Jr."}],
"issued": {"date-parts": [[2015]]}}),
("only-stop-words", [], {"title": "The of a", "author": [{"family": "Lee", "given": "Jo"}],
"issued": {"date-parts": [[2019]]}}),
("no-year", [], {"title": "Untitled", "author": [{"family": "Lee"}]}),
("no-author", [], {"title": "Fluids", "issued": {"date-parts": [[2020]]}}),
("non-latin-author", [], {"title": "Fluids", "author": [{"family": "\u5f20\u4f1f"}], "issued": {"date-parts": [[2020]]}}),
("hale", hale["src"]["document"]["work"]["same_as"], hale["src"]["document"]["meta"]),
]:
ident = K.identity(same_as, meta)
kc.append({"id": cid, "same_as": same_as, "meta": meta, "scholar": K.scholar_part(meta), "identity": ident,
"payload": K.SCHEME + ":" + ident, "sha256": H(K.SCHEME + ":" + ident), "suffix": K.suffix(ident),
"suffix_8": K.suffix(ident, 8), "key": K.work_key(same_as, meta)})
hk = hale["document"]["work"]["key"]
for cid, version in [("version-of-record", {"type": "version-of-record"}), ("supplement", {"type": "supplement"}),
("preprint-v2", {"type": "preprint", "seq": 2}), ("draft-v3", {"type": "draft", "seq": 3}),
("erratum", {"type": "erratum"}), ("accepted-no-seq", {"type": "accepted"})]:
kc.append({"id": "document-" + cid, "work_key": hk, "version": version, "document_key": K.document_key(hk, version)})
kc.append({"id": "collision", "same_as": attn and ["doi:10.48550/arXiv.1706.03762"], "meta": attn,
"taken": [K.work_key(["doi:10.48550/arXiv.1706.03762"], attn)],
"key": K.unique_work_key(["doi:10.48550/arXiv.1706.03762"], attn, {K.work_key(["doi:10.48550/arXiv.1706.03762"], attn)})})
wv(out, "keys.jsonl", kc)
# Proofs (RFC 9162): inclusion for every item of the Merkle vectors, consistency for every prefix, and the
# example paper's correction history.
pf = [{"id": "empty-history", "kind": "root", "items": [], "root": crypto.history_root([])}]
for n in (1, 2, 3, 4, 5, 7):
items = [["node", mint_node_id("m", "sentence", H(str(i)), 0), H("h%d" % i)] for i in range(n)]
root = crypto.merkle_root(items)
for i in range(n):
pf.append({"id": "inclusion-%d-of-%d" % (i, n), "kind": "inclusion", "items": items, "index": i, "size": n,
"root": root, "leaf": crypto.leaf_hash(items[i]), "path": crypto.inclusion_path(items, i)})
for m in range(n + 1):
pf.append({"id": "consistency-%d-to-%d" % (m, n), "kind": "consistency", "items": items, "from": m, "to": n,
"from_root": crypto.history_root(items[:m]), "to_root": root,
"path": crypto.consistency_proof(items, m)})
hist = history_items(hale["records"])
pf.append({"id": "hale-history-8-to-12", "kind": "consistency", "items": hist, "from": 8, "to": len(hist),
"from_root": crypto.history_root(hist[:8]), "to_root": crypto.history_root(hist),
"path": crypto.consistency_proof(hist, 8)})
pf.append({"id": "hale-history-correction-5", "kind": "inclusion", "items": hist, "index": 4, "size": len(hist),
"root": crypto.history_root(hist), "leaf": crypto.leaf_hash(hist[4]), "path": crypto.inclusion_path(hist, 4)})
wv(out, "proofs.jsonl", pf)
# The VRF (RFC 9381, ECVRF-EDWARDS25519-SHA512-TAI): the RFC's Example 16, two more inputs for its key, and
# the spot-check seed of the example paper's first receipt.
vv = []
for cid, alpha in (("rfc9381-example-16", b""), ("rfc9381-key-alpha-72", b"\x72"), ("rfc9381-key-alpha-af82", b"\xaf\x82")):
pi = vrf.prove(sk, alpha)
vv.append({"id": cid, "secret": sk.hex(), "public": vrf.public_key(sk).hex(), "alpha": alpha.hex(),
"proof": pi.hex(), "beta": vrf.proof_to_hash(pi).hex()})
seed = base64.b64decode(q1["qa"]["seed"])
vv.append({"id": "ben-spot-seed-q1", "public": base64.b64decode(ROSTER_BY_ID["ben-spot"]["public"]).hex(),
"alpha": RC.seed_payload(r1).hex(), "proof": seed.hex(), "beta": vrf.proof_to_hash(seed).hex()})
bad = bytearray(seed)
bad[40] ^= 1
vv.append({"id": "tampered-challenge", "public": base64.b64decode(ROSTER_BY_ID["ben-spot"]["public"]).hex(),
"alpha": RC.seed_payload(r1).hex(), "proof": bytes(bad).hex(), "beta": None})
vv.append({"id": "other-input", "public": base64.b64decode(ROSTER_BY_ID["ben-spot"]["public"]).hex(),
"alpha": (RC.seed_payload(r1) + b"x").hex(), "proof": seed.hex(), "beta": None})
wv(out, "vrf.jsonl", vv)
# A checkpoint of the team's log (spec/23): every receipt and revocation of the example paper, then the roster.
entries = [LG.entry(r) for r in hale["receipts"]] + [LG.entry(ROSTER)]
note = LG.sign_checkpoint("scient.ing/log/smithlab", entries, [("ana", secret("ana"))])
wv(out, "checkpoint.jsonl", [{"id": "smithlab", "origin": "scient.ing/log/smithlab", "entries": entries,
"root": LG.root(entries), "signers": ["ana"], "note": note}])
# Alignment.
def item(i, runs, container, context="paragraph"):
doc, s = sentence_doc(runs)
st, cx = hashing.strict(doc, s)
return {"id": i, "runs": runs, "strict": st, "lossy": hashing.lossy_from_strict(st), "complex": cx,
"container": container, "context": context}
A = [item("a1", [T("Methods")], "h", "heading"),
item("a2", [T("Every bond was scanned in steps of 0.01 \u00c5 around its minimum.")], "p1"),
item("a3", [T("where "), M("\\frac{a}{b}"), T(" is the bond length.")], "p1"),
item("a4", [T("The fitted rate is 10\u00b3 per second at room temperature.")], "p1"),
item("a5", [T("where "), M("\\frac{c}{d}"), T(" is the bond length.")], "p2"),
item("a6", [T("This sentence was dropped by the second reading entirely.")], "p2"),
item("a7", [T("The last sentence of the section is unique and long enough.")], "p2")]
B = [item("b1", [T("Methods")], "x", "paragraph"),
item("b2", [T("Every bond was scanned in steps of 0.01 \u00c5 around its minimum.")], "q1"),
item("b3", [T("where "), M("\\frac{a}{b}"), T(" is the bond length.")], "q1"),
item("b4", [T("The fitted rate is 103 per second at room temperature.")], "q2"),
item("b5", [T("where "), M("\\frac{c}{e}"), T(" is the bond length.")], "q2"),
item("b6", [T("A sentence only the second reading has, also long.")], "q2"),
item("b7", [T("The last sentence of the section is unique and long enough.")], "q2")]
wv(out, "alignment.jsonl", [{"id": "two-readings", "a": A, "b": B, "expect": AL.align(A, B)}])
# Corrections, op by op, on the tiny document.
wv(out, "corrections.jsonl", corrections_vectors())
# Anchor resolution against the hale package.
wv(out, "resolve.jsonl", resolve_vectors(hale))
# Rights.
wv(out, "rights.jsonl", rights_vectors(hale))
def corrections_vectors():
tiny = tiny_package(None, receipts=False)
st0 = tiny["final"]
nm = tiny["names"].ids
crop = [{"kind": "crop", "sha256": crop_sha("tiny")}]
person = [{"kind": "person", "by": "ana"}]
base = {"author": {"key": "ana"}, "reason": "Vector.", "at": "2026-10-11T12:00:00Z"}
cases = [
("replace-digit", {"op": "replace", "target": nm["@a"], "find": "42.0", "replace": "42.5", "evidence": crop}),
("replace-digit-no-crop", {"op": "replace", "target": nm["@a"], "find": "42.0", "replace": "42.5", "evidence": person}),
("replace-word", {"op": "replace", "target": nm["@c"], "find": "second", "replace": "later", "evidence": person}),
("replace-not-unique", {"op": "replace", "target": nm["@a"], "find": "0", "replace": "1", "evidence": crop}),
("replace-runs", {"op": "replace", "target": nm["@a"], "find": "300 K", "replace": [
{"kind": "math", "notation": "tex", "source": "300\\,\\mathrm{K}"}], "evidence": crop}),
("insert-text", {"op": "insert", "target": nm["@d"], "run": 0, "offset": 8, "text": "only ", "evidence": crop}),
("insert-text-no-crop", {"op": "insert", "target": nm["@d"], "run": 0, "offset": 8, "text": "only ",
"evidence": person}),
("insert-text-bad-run", {"op": "insert", "target": nm["@d"], "run": 7, "offset": 0, "text": "x",
"evidence": crop}),
("insert-node", {"op": "insert", "parent": nm["@p2"], "place_after": nm["@c"],
"node": {"kind": "sentence", "content": [{"kind": "text", "text": "A dropped sentence."}]},
"evidence": crop}),
("delete", {"op": "delete", "target": nm["@p2"], "evidence": person}),
("delete-digits-no-crop", {"op": "delete", "target": nm["@p1"], "evidence": person}),
("split", {"op": "split", "target": nm["@a"], "offset": 13, "evidence": person}),
("split-inside-xref", {"op": "split", "target": nm["@b"], "offset": 6, "evidence": person}),
("merge", {"op": "merge", "target": nm["@c"], "with": nm["@d"], "joiner": " ", "evidence": person}),
("merge-not-adjacent", {"op": "merge", "target": nm["@a"], "with": nm["@c"], "evidence": person}),
("move", {"op": "move", "target": nm["@d"], "parent": nm["@p1"], "place_after": None, "evidence": person}),
("move-after-itself", {"op": "move", "target": nm["@d"], "parent": nm["@p2"], "place_after": nm["@d"],
"evidence": person}),
("reclassify", {"op": "reclassify", "target": nm["@p2"], "kind": "footnote", "evidence": person}),
("reclassify-blocks", {"op": "reclassify", "target": nm["@p2"], "kind": "quote", "evidence": person}),
("reclassify-bad", {"op": "reclassify", "target": nm["@a"], "kind": "paragraph", "evidence": person}),
("relabel-section", {"op": "relabel", "target": nm["@sec"], "label": "2", "evidence": crop}),
("relabel-remove", {"op": "relabel", "target": nm["@sec"], "label": None, "evidence": crop}),
("relabel-digit-no-crop", {"op": "relabel", "target": nm["@t"], "label": "Table 2", "evidence": person}),
("set-colspan", {"op": "set", "target": nm["@y1"], "attr": "colspan", "value": 2, "evidence": person}),
("set-bad-value", {"op": "set", "target": nm["@y1"], "attr": "colspan", "value": "2", "evidence": person}),
("set-not-settable", {"op": "set", "target": nm["@y1"], "attr": "level", "value": 2, "evidence": person}),
("insert-node-junk", {"op": "insert", "parent": nm["@p2"], "place_after": nm["@c"],
"node": {"kind": "sentence", "content": [{"kind": "text", "text": "Junk."}], "foo": 1},
"evidence": crop}),
("delete-header-cell", {"op": "delete", "target": nm["@hx"], "evidence": person}),
]
out = []
vals0 = hashing.compute(st0.doc())
before = [dict(n, **vals0[n["id"]]) for n in st0.nodes]
for cid, c in cases:
c = dict(base, **c)
case = {"id": cid, "doc": st0.key, "before": {"nodes": before, "ids": [], "rev": 0}, "correction": c}
try:
st1, rec = CO.apply(st0, c, store=SCHEMAS)
v1 = hashing.compute(st1.doc())
case["expect"] = {"record": rec, "nodes": [dict(n, **v1[n["id"]]) for n in st1.nodes], "ids": st1.ids}
except Refusal as e:
case["expect"] = {"refusal": e.rule}
out.append(case)
return out
def resolve_vectors(hale):
src, names, states, final = hale["src"], hale["names"], hale["states"], hale["final"]
s0 = states[0]
d0 = s0.doc()
v0 = hashing.compute(d0)
out = []
for cid, a in src["anchors_at_rev0"].items():
nid = names.ids[a["node"]]
if a.get("container_of"):
nid = d0.nodes[nid]["parent"]
span = None
if "quote" in a:
t = hashing.plain(d0, nid)
i = t.index(a["quote"])
span = [i, i + len(a["quote"])]
anchor = AN.make_anchor(d0, v0, nid, 0, span=span, store=STORE)
if "alias" in a:
anchor["node"] = a["alias"]
res = AN.resolve(final, anchor)
out.append({"id": cid, "package": "fixtures/packages/" + hale["key"], "anchor": anchor, "expect": res})
return out
def rights_vectors(hale):
out = OUT
w = hale["witness"]
stages = json.load(open(os.path.join(ROOT, "registry", "stages", "scienting-stages-default.json")))["stages"]
policy = json.load(open(os.path.join(out, "fixtures", "records", "policy.json")))
cases = []
for cid, stage, proc, now, pols in [
("convert-local", "convert-model", "local", "2026-10-11T00:00:00Z", []),
("convert-outside", "convert-model", "outside", "2026-10-11T00:00:00Z", []),
("review-tenancy-unknown", "review-model", "tenancy", "2026-10-11T00:00:00Z", []),
("review-tenancy-policy", "review-model", "tenancy", "2026-10-11T00:00:00Z", [policy]),
("index", "index", "local", "2026-10-11T00:00:00Z", []),
("embed-outside-refused", "embed", "outside-no-retention", "2026-10-11T00:00:00Z", [policy]),
("publish-quotes", "publish-quotes", "local", "2026-10-11T00:00:00Z", []),
("read-expired", "read", "local", "2027-09-01T00:00:00Z", []),
("accessible-copy-unknown", "accessible-copy", "local", "2026-10-11T00:00:00Z", []),
]:
uses, expired = RI.effective(w, pols, now)
ok, lacking = RI.check(stages[stage], proc, uses)
cases.append({"id": cid, "rights": w["rights"], "origin": w["origin"], "witness_sha256": w["sha256"],
"policies": pols, "stage": stage, "requires": stages[stage], "processor": proc, "now": now,
"expect": {"allowed": ok, "lacking": lacking, "expired": expired}})
oa = rights_record("open")
sub = w["rights"]
cases.append({"id": "combine", "rights": [oa, sub],
"expect": RI.combine([RI.flat(oa), RI.flat(sub)])})
backends = [json.load(open(os.path.join(out, "fixtures", "records", "backend-%s.json" % b))) for b in ("docling", "examplevendor")]
profiles = [json.load(open(os.path.join(out, "fixtures", "records", "profile-%s.json" % p))) for p in ("smithlab", "acme")]
for b in backends:
for p in profiles:
ok, failed = RI.backend_allowed(b, p)
cases.append({"id": "backend-%s-%s" % (b["name"], p["team"]), "backend": b, "profile": p,
"expect": {"allowed": ok, "failed": failed}})
return cases
# --- records ------------------------------------------------------------------------
def wr(out, rel, rec):
path = os.path.join(out, "fixtures", "records", rel)
os.makedirs(os.path.dirname(path), exist_ok=True)
with open(path, "wb") as f:
f.write((jcs.dumps(rec) + "\n").encode() if not isinstance(rec, bytes) else rec)
CTX = ["http://www.w3.org/ns/anno.jsonld", "https://paperspec.scient.ing/context/scienting-1.jsonld"]
def ann_hash(a):
return H(jcs.dumpb({k: v for k, v in a.items() if k not in ("status", "receipt", "modified")}))
TAG = "tag:scient.ing,2026:"
def records(out, hale):
names, final = hale["names"], hale["final"]
doc = final.doc()
vals = hashing.compute(doc)
rev = final.rev
I = names.ids
HALE = hale["key"]
QHALE = STORE + "/" + HALE
OKAFOR = Names.registry["okafor2025open"].key
DRAFT = Names.registry["smithlab2027charges"].key
def anchor(name, quote=None):
nid = I[name]
span = None
if quote:
t = hashing.plain(doc, nid)
i = t.index(quote)
span = [i, i + len(quote)]
return AN.make_anchor(doc, vals, nid, rev, span=span, store=STORE)
wr(out, "roster.json", ROSTER)
# The doubtful reading behind the first correction, as the converter raised it before anyone answered it.
sus = {"format": "scienting-suspect/1", "kind": "number", "node": I["@s4"], "a": "10-3", "b": "10\u22123",
"crop": crop_sha(I["@s4"]),
"note": "The text layer reads a hyphen on the line; the page image shows a raised minus three."}
sus = dict(sus, id=record_id(sha256_hex(jcs.dumpb(sus))))
wr(out, "suspect.json", sus)
wr(out, "anchor-span.json", anchor("@s3", "first test case"))
wr(out, "anchor-cell.json", anchor("@cc_ak"))
wr(out, "anchor-paragraph.json", AN.make_anchor(doc, vals, doc.nodes[I["@s3"]]["parent"], rev, store=STORE))
long_anchor = AN.make_anchor(doc, vals, I["@s2"], rev, store=STORE)
wr(out, "anchor-leaf.json", long_anchor)
# A public layer's anchor on a paywalled paper: keyed by the pages, no text.
fps = [p["page_fp"] for p, _ in hale["pages"]]
layer_id = TAG + "layer:smithlab/kp4m2x7q"
pk = crypto.page_key(fps, "layer:smithlab/kp4m2x7q")
hk = hashing.Hasher(pk)
ka = anchor("@cc_ak")
keyed_vals = hashing.compute(doc, hk)
ka = {"format": "scienting-anchor/1", "store": STORE, "doc": ka["doc"], "node": ka["node"], "kind": "cell",
"own_hash": keyed_vals[ka["node"]]["own_hash"], "node_hash": keyed_vals[ka["node"]]["node_hash"],
"quote": {"mac": hk(norm(ka["quote"]["exact"])), "length": len(ka["quote"]["exact"])},
"context": {"table_label_mac": hk(norm(ka["context"]["table_label"])),
"row_path_mac": [hk(norm(x)) for x in ka["context"]["row_path"]],
"column_path_mac": [hk(norm(x)) for x in ka["context"]["column_path"]]},
"rev": rev, "keyed": {"scheme": "hkdf-pages", "context": "layer:smithlab/kp4m2x7q",
"pages": [crypto.page_hash(f) for f in fps]}}
wr(out, "anchor-keyed.json", ka)
# Rights, policy, backends, profiles.
wr(out, "rights-open.json", rights_record("open"))
wr(out, "rights-subscription.json", rights_record("subscription"))
ccc = rights_record("subscription")
ccc.update({"license": {"text": "Annual collective license with internal AI re-use (fictional)."},
"basis": "collective-license", "valid_until": "2027-06-30T23:59:59Z"})
wr(out, "rights-collective.json", ccc)
policy = RC.seal({"format": "scienting-policy/1", "project": "smithlab-charges",
"applies_to": {"basis": ["subscription"], "doi_prefixes": ["10.5555/"]},
"uses": {"ai": {"internal/tenancy": True}},
"statement": "Our institutional agreement covers internal AI use in our own cloud account for "
"these papers (fictional).",
"by": "ana", "at": "2026-10-05T09:00:00Z", "valid_until": "2027-08-31T23:59:59Z"},
secret("ana"), "ana")
wr(out, "policy.json", policy)
wr(out, "backend-docling.json", {
"format": "scienting-backend/1", "name": "docling", "version": "illustrative", "stage": "read",
"processor": "local", "code_license": {"spdx": "MIT"},
"weights": [{"name": "layout model", "license": {"spdx": "Apache-2.0"}}], "conditions": []})
wr(out, "backend-examplevendor.json", {
"format": "scienting-backend/1", "name": "examplevendor", "version": "1.0", "stage": "read",
"processor": "local", "code_license": {"spdx": "Apache-2.0"},
"weights": [{"name": "example weights", "license": {"text": "A fictional weight license with conditions."}}],
"conditions": [{"id": "revenue-cap", "kind": "revenue-cap", "usd": 5000000},
{"id": "no-competition", "kind": "no-competition", "vendor": "examplevendor"},
{"id": "purpose", "kind": "purpose", "allowed": ["research", "personal", "education", "startup"]}]})
wr(out, "profile-smithlab.json", {"format": "scienting-profile/1", "team": "smithlab", "purpose": "research",
"revenue_or_funding_usd": {"at_most": 2000000}, "competes_with": [],
"recorded_by": "ana", "at": "2026-10-01T00:00:00Z"})
wr(out, "profile-acme.json", {"format": "scienting-profile/1", "team": "acme", "purpose": "commercial",
"revenue_or_funding_usd": {"at_most": None}, "competes_with": None,
"recorded_by": "acme-admin", "at": "2026-10-01T00:00:00Z"})
# A layer with one annotation of each common kind.
creator = {"id": TAG + "key:ana", "type": "Person", "name": "Ana Example"}
model = {"id": TAG + "software:example-model", "type": "Software", "name": "A model (illustrative)"}
def target(a):
return {"type": "SpecificResource", "source": TAG + "doc:" + QHALE,
"selector": [{"type": "AnchorSelector", "anchor": a}]
+ ([{"type": "TextQuoteSelector", "exact": a["quote"]["exact"],
"prefix": a["quote"].get("prefix", ""), "suffix": a["quote"].get("suffix", "")}]
if "quote" in a and "exact" in a["quote"] else [])}
def ann(body, tgt, motivation, status="proposed", who=creator, created="2026-10-11T19:00:00Z"):
a = {"@context": CTX, "type": "Annotation", "layer": layer_id, "motivation": motivation, "creator": who,
"created": created, "body": body, "target": tgt}
a["id"] = TAG + "annotation:smithlab/" + record_id(H(jcs.dumpb(a)))
a["status"] = status
return a
ex_body = {"format": "scienting-exercise/1", "exercise": "anchor",
"prompt": "Find the table cell that gives the Set A bond constant for the CT–CT bond.",
"key": {"elements": [{"anchors": [anchor("@cc_ak")], "points": "1",
"feedback": {"hit": "That is the cell.", "miss": "Look in Table 1, row CT–CT."}}]},
"rule": {"match": "same-node"}}
check_body = {"format": "scienting-exercise/1", "exercise": "check",
"prompt": "Does the cited passage support the claim that harmonic terms are the most common choice?",
"key": {"verdict": {"value": "supports", "points": "1"},
"elements": [{"anchors": [anchor("@s1", "the most common description")], "points": "1"}]},
"rule": {"match": "overlap", "min_overlap": "0.5"}}
anns = [
ann({"type": "TextualBody", "kind": "gloss", "audience": "novice", "format": "text/markdown", "language": "en",
"value": "A *harmonic* term treats a bond like a spring: the energy grows with the square of the stretch."},
target(anchor("@s2", "bond force constant")), "describing", "confirmed"),
ann({"type": "SpecificResource", "kind": "claim-link", "source": "lode:amber/k-bond", "pin": "illustrative-pin",
"role": "states"}, target(anchor("@s2")), "linking", "confirmed"),
ann({"type": "TextualBody", "kind": "comment", "value": "The step size matters more than the text admits."},
target(anchor("@s6a", "steps of 0.01")), "commenting"),
ann({"type": "TextualBody", "kind": "question", "for": "group",
"value": "Why fit to a scan first and to frequencies second?"},
target(AN.make_anchor(doc, vals, I["@sec2"], rev, store=STORE)), "questioning"),
ann({"type": "TextualBody", "kind": "citation-context", "reference": I["@r2"], "verdict": "cannot-tell",
"reason": "value", "value": "Cited for the band position; the reference is not in the library."},
target(anchor("@s3", "1595")), "assessing"),
ann({"type": "TextualBody", "kind": "generated", "value": "Two panels: an energy scan and its fit residual.",
"generator": {"model": "example-model", "run": "illustrative-run-1"}},
target(AN.make_anchor(doc, vals, I["@fig1"], rev, store=STORE)), "describing", "proposed", model),
ann({"type": "TextualBody", "kind": "exercise", "value": ex_body["prompt"], "exercise": ex_body},
target(AN.make_anchor(doc, vals, I["@tab1"], rev, store=STORE)), "assessing", "confirmed"),
ann({"type": "TextualBody", "kind": "exercise", "value": check_body["prompt"], "exercise": check_body},
target(anchor("@s1")), "assessing", "confirmed"),
# A person's description of the figure, which an accessible export may use because it is confirmed.
ann({"type": "TextualBody", "kind": "description", "role": "alt", "language": "en",
"value": "Energy along the C–C bond stretch (panel a) and the fit residual (panel b)."},
target(AN.make_anchor(doc, vals, I["@fig1"], rev, store=STORE)), "describing", "confirmed"),
]
anns.append(ann({"type": "TextualBody", "kind": "comment", "value": "Agreed; see the residual panel."},
{"type": "SpecificResource", "source": anns[2]["id"]}, "replying", created="2026-10-11T19:05:00Z"))
confirmed = [a for a in anns if a["status"] == "confirmed"]
lr = RC.seal({"format": "scienting-receipt/1", "doc": HALE, "rev": rev, "level": "F3",
"scope": "targeted:layer", "nodes": [],
"records": sorted([["annotation", a["id"].rsplit("/", 1)[1], ann_hash(a)] for a in confirmed]),
"evidence": [], "by": "ana", "at": "2026-10-11T19:30:00Z"}, secret("ana"), "ana")
for a in confirmed:
a["receipt"] = lr["id"]
wr(out, "layer/layer.json", {"@context": CTX, "format": "scienting-layer/1", "id": layer_id,
"type": "AnnotationCollection", "label": "Smith lab reading of Hale and Osei",
"purpose": "Glosses, claim links and exercises for new students.",
"audience": "student", "visibility": "team", "license": {"spdx": "CC-BY-4.0"},
"creator": creator, "created": "2026-10-11T19:00:00Z", "project": "smithlab-charges",
"docs": [QHALE], "total": len(anns)})
wr(out, "layer/annotations.jsonl", jsonl(anns))
wr(out, "layer/receipt.json", lr)
# Submissions and grades.
ex_ann = anns[6]
sub_ok = {"format": "scienting-submission/1", "exercise": ex_ann["id"], "doc": QHALE,
"rev": rev, "student": "s-7k2m", "answers": {"anchors": [anchor("@cc_ak")]},
"submitted_at": "2026-10-20T10:00:00Z"}
sub_ok["id"] = record_id(H(jcs.dumpb(sub_ok)))
sub_miss = dict(sub_ok, student="s-9q4p", answers={"anchors": [anchor("@cc_bk")]})
sub_miss.pop("id")
sub_miss["id"] = record_id(H(jcs.dumpb(sub_miss)))
ch_ann = anns[7]
sub_check = {"format": "scienting-submission/1", "exercise": ch_ann["id"], "doc": QHALE,
"rev": rev, "student": "s-7k2m",
"answers": {"verdict": "supports", "anchors": [anchor("@s1", "most common description of covalent")]},
"submitted_at": "2026-10-20T10:05:00Z"}
sub_check["id"] = record_id(H(jcs.dumpb(sub_check)))
grades = []
for name, sub, body in (("anchor-hit", sub_ok, ex_body), ("anchor-miss", sub_miss, ex_body),
("check-overlap", sub_check, check_body)):
g = GR.grade(body, sub, final)
wr(out, "submissions/%s.json" % name, sub)
wr(out, "grades/%s.json" % name, g)
grades.append({"id": name, "package": "fixtures/packages/" + hale["key"], "exercise": body,
"submission": sub, "expect": g})
wv(out, "grading.jsonl", grades)
# Project.
wr(out, "project.json", {
"format": "scienting-project/1", "key": "smithlab-charges", "store": STORE, "name": "Charges and bonds",
"members": [{"person": "Ana Example", "role": "owner", "keys": ["ana"]},
{"person": "Ben Example", "role": "editor", "keys": ["ben", "ben-spot"]},
{"person": "Cara Example", "role": "reader", "keys": ["cara"]}],
"documents": [{"doc": QHALE, "reason": "The bond constants we start from.",
"tags": ["parameters"], "status": {"Ben Example": "read", "Cara Example": "reading"}},
{"doc": OKAFOR, "reason": "Solvent shifts for the validation set.", "tags": ["spectra"]}],
"paths": [{"id": "onboarding", "title": "First week", "steps": [
{"type": "document", "ref": QHALE, "note": "Read the introduction."},
{"type": "node", "ref": QHALE + "#" + I["@tab1"], "note": "Then the table."},
{"type": "claim", "ref": "lode:amber/k-bond"}]}],
"layers": [layer_id], "datasets": ["ds-hale-bond-params", "ds-hale-ctct-scan"], "views": ["ctct-scan"],
"drafts": [STORE + "/" + DRAFT], "claims": {"lode": "amber"},
"settings": {"digit_rule": "anchored-sections", "drift_threshold": "0.25",
"spot_check": {"rate": "0.05", "minimum": 10}, "rights_unknown": "refuse",
"stages": "scienting-stages-default"}})
# A digitization of panel a, and the dataset it yields.
p2 = hale["pages"][1][0]
pa = I["@pa"]
ft = {t["text"]: t["id"] for t in doc.nodes[pa]["attrs"]["figure_text"]}
pts = [((110.0, 360.0), ("1.45", "3.10")), ((150.0, 330.0), ("1.50", "0.40")), ((190.0, 300.0), ("1.55", "2.80"))]
dg = {"format": "scienting-digitization/1", "doc": QHALE,
"target": {"anchor": AN.make_anchor(doc, vals, pa, rev, store=STORE), "page_fp": p2["page_fp"],
"region": [72.0, 260.0, 300.0, 410.0], "witness": hale["witness"]["sha256"]},
"axes": [{"id": "x", "scale": "linear", "unit": "angstrom",
"calibration": [{"at": [90.0, 400.0], "value": "1.4"}, {"at": [210.0, 400.0], "value": "1.6"}],
"check": {"at": [150.0, 400.0], "value": "1.5"}, "label": {"panel": pa, "figure_text": ft["r (Å)"]}},
{"id": "y", "scale": "linear", "unit": "kcal / mol",
"calibration": [{"at": [80.0, 400.0], "value": "0"}, {"at": [80.0, 280.0], "value": "4"}],
"label": {"panel": pa, "figure_text": ft["E (kcal mol⁻¹)"]}}],
"series": [{"id": "scan", "legend": "scan", "mark": {"color": "#285aa0", "marker": "circle"}, "kind": "points",
"values": [{"at": list(at), "value": {"x": x, "y": y},
"uncertainty": {"x": "0.01", "y": "0.05", "resolution": "0.01", "marker": "0.02",
"calibration": "0.01"}} for at, (x, y) in pts]}],
"method": {"kind": "manual", "tool": {"name": "hand", "version": "0"}},
"verification": {"image": "", "residual": "0.4", "max_residual": "0.9"},
"confirmation": None, "dataset": "ds-hale-ctct-scan", "rev": rev}
dg["verification"]["image"] = crop_sha("overlay of panel a")
dg["id"] = record_id(H(jcs.dumpb(dg)))
wr(out, "digitization.json", dg)
body = {k: v for k, v in dg.items() if k != "confirmation"}
conf = RC.seal({"format": "scienting-receipt/1", "doc": HALE, "rev": rev, "level": "F3",
"scope": "targeted:digitization", "nodes": [],
"records": [["digitization", dg["id"], H(jcs.dumpb(body))]],
"evidence": [{"kind": "crop", "node": pa, "sha256": crop_sha("overlay of panel a")}],
"by": "ana", "at": "2026-10-11T20:00:00Z"}, secret("ana"), "ana")
wr(out, "digitization-confirmation.json", conf)
ds = {"format": "scienting-dataset/1", "id": "ds-hale-ctct-scan", "name": "hale-ctct-scan",
"title": "Energy along the C–C stretch, read from Figure 1a",
"resources": [{"name": "scan", "path": "data.csv", "format": "csv", "encoding": "utf-8",
"schema": {"fields": [{"name": "point", "type": "string"},
{"name": "r", "type": "number", "unit": "angstrom",
"uncertainty": {"field": "r_u", "kind": "resolution"}},
{"name": "r_u", "type": "number", "unit": "angstrom"},
{"name": "e", "type": "number", "unit": "kcal / mol",
"uncertainty": {"field": "e_u", "kind": "resolution"}},
{"name": "e_u", "type": "number", "unit": "kcal / mol"}],
"primaryKey": "point"},
"origin": {"kind": "digitized", "digitization": dg["id"], "anchors": "anchors.jsonl"}}]}
csvd = "point,r,r_u,e,e_u\n" + "".join("p%d,%s,0.01,%s,0.05\n" % (i, x, y) for i, (_, (x, y)) in enumerate(pts))
va = [{"resource": "scan", "row": "p%d" % i, "field": f, "digitization": dg["id"], "series": "scan", "point": i}
for i in range(len(pts)) for f in ("r", "e")]
wr(out, "datasets/ds-hale-ctct-scan/datapackage.json", ds)
wr(out, "datasets/ds-hale-ctct-scan/data.csv", csvd.encode())
wr(out, "datasets/ds-hale-ctct-scan/anchors.jsonl", jsonl(va))
ds_files = sorted([["anchors.jsonl", H(jsonl(va)), len(jsonl(va))], ["data.csv", H(csvd), len(csvd.encode())],
["datapackage.json", H(jcs.dumps(ds) + "\n"), len((jcs.dumps(ds) + "\n").encode())]])
ds_hash = H(jcs.dumpb({"format": "scienting-dataset/1", "files": ds_files}))
wr(out, "view.vl.json", {
"$schema": "https://vega.github.io/schema/vega-lite/v5.json",
"usermeta": {"scienting": {"format": "scienting-view/1", "id": "ctct-scan",
"data": [{"name": "ds-hale-ctct-scan", "dataset": "ds-hale-ctct-scan",
"hash": ds_hash, "resource": "scan"}]}},
"data": {"name": "ds-hale-ctct-scan"}, "mark": {"type": "point"},
"encoding": {"x": {"field": "r", "type": "quantitative", "title": "r (Å)"},
"y": {"field": "e", "type": "quantitative", "title": "E (kcal/mol)"}}})
# QA report.
q1 = hale["by_name"]["q1"]
q2 = hale["by_name"]["q2"]
n = len(q1["qa"]["sample"]) + len(q2["qa"]["sample"])
k = len(q1["qa"]["errors"]) + len(q2["qa"]["errors"])
wr(out, "qa-report.json", RC.seal({
"format": "scienting-qa-report/1", "delivery": "smithlab evidence pilot (fictional)",
"docs": [QHALE], "receipts": [hale["by_name"]["r1"]["id"], hale["by_name"]["r4"]["id"]],
"reviewers": [{"key": "ana", "sampled": n, "errors": k, "confidence": "0.95", "method": "clopper-pearson",
"bound": RC.bound_string(k, n)}],
"by": "ana", "at": "2026-10-12T10:00:00Z"}, secret("ana"), "ana"))
# A checklist for an accessible HTML export of the paper, and the receipt that covers it (spec/08 §8.11).
html_sha = H("accessible HTML export of %s (synthetic)" % HALE)
checker = {"name": "example-checker", "version": "1.0"}
checklist = {"format": "scienting-checklist/1", "doc": QHALE, "rev": rev,
"standard": {"name": "WCAG 2.1", "level": "AA", "url": "https://www.w3.org/TR/WCAG21/"},
"export": {"kind": "html", "sha256": html_sha, "size": 48213},
"items": [
{"criterion": "1.1.1", "result": "pass", "method": "manual",
"note": "Every figure has a confirmed description in the layer."},
{"criterion": "1.2.1", "result": "not-applicable", "method": "manual", "note": "No audio or video."},
{"criterion": "1.3.1", "result": "pass", "method": "manual",
"note": "Headings, lists and table headers are marked up; each cell names its header path."},
{"criterion": "1.3.2", "result": "pass", "method": "automated", "tool": checker},
{"criterion": "1.4.3", "result": "pass", "method": "automated", "tool": checker},
{"criterion": "1.4.5", "result": "pass", "method": "manual",
"note": "Equations are MathML, never images of text."},
{"criterion": "2.4.2", "result": "pass", "method": "automated", "tool": checker},
{"criterion": "3.1.1", "result": "pass", "method": "automated", "tool": checker}],
"by": "ana", "at": "2026-10-12T11:00:00Z"}
checklist_hash = H(jcs.dumpb(checklist))
checklist["id"] = record_id(checklist_hash)
wr(out, "checklist/checklist.json", checklist)
wr(out, "checklist/receipt.json", RC.seal({
"format": "scienting-receipt/1", "doc": HALE, "rev": rev, "level": "F3",
"scope": "targeted:checklist", "nodes": [], "records": [["checklist", checklist["id"], checklist_hash]],
"evidence": [{"kind": "source-file", "sha256": html_sha, "note": "The exported HTML."}],
"by": "ana", "at": "2026-10-12T11:30:00Z"}, secret("ana"), "ana"))
# The target set of targeted F3 (spec/08 §8.10) under each digit rule: the dataset's cells, the sentence a
# confirmed claim link anchors, and the panel a digitization reads from are the anchored nodes.
dataset_anchors = [json.loads(x) for x in
hale["files"]["datasets/ds-hale-bond-params/anchors.jsonl"].decode().splitlines()]
anchored = sorted({a["anchor"]["node"] for a in dataset_anchors} | {I["@s2"], pa})
wv(out, "targets.jsonl", [{"id": "hale-" + rule, "package": "fixtures/packages/" + hale["key"],
"anchored": anchored, "digit_rule": rule,
"expect": {"nodes": TG.target_set(doc, anchored, rule)}}
for rule in TG.DIGIT_RULES])
# Proofs (spec/08 §8.5, spec/11 §11.5): one node of the first receipt was covered, and revision 12 extends
# revision 8 of the correction history.
r1 = hale["by_name"]["r1"]
items = crypto.receipt_items(r1)
wr(out, "proof-inclusion.json", {"format": "scienting-proof/1", "kind": "inclusion", "receipt": r1["id"],
"item": items[3], "index": 3, "size": len(items), "root": r1["root"],
"path": crypto.inclusion_path(items, 3)})
hist = history_items(hale["records"])
wr(out, "proof-consistency.json", {"format": "scienting-proof/1", "kind": "consistency", "doc": QHALE,
"from_rev": 8, "to_rev": len(hist), "from_root": crypto.history_root(hist[:8]),
"to_root": hale["document"]["history"], "path": crypto.consistency_proof(hist, 8)})
# A checkpoint of the team's log (spec/23), as the parsed form of its signed note.
entries = [LG.entry(r) for r in hale["receipts"]] + [LG.entry(ROSTER)]
note = LG.sign_checkpoint("scient.ing/log/smithlab", entries, [("ana", secret("ana"))])
parsed = LG.parse_checkpoint(note)
wr(out, "checkpoint.json", {"format": LG.FORMAT, "origin": parsed["origin"], "size": parsed["size"],
"root": parsed["root"], "note": note,
"signatures": [{"name": n, "key_hash": kh.hex(), "value": base64.b64encode(sig).decode()}
for n, kh, sig in parsed["signatures"]]})
# Draft snapshot and sentence IDs.
s5 = I["@s5"]
snap_anchor = AN.make_anchor(doc, vals, s5, rev, store=STORE)
wr(out, "snapshot.json", {"format": "scienting-snapshot/1", "store": STORE, "made_at": "2026-10-11T21:00:00Z",
"quotes": [{"address": AN.format_address(STORE, HALE, s5, vals[s5]["node_hash"]),
"anchor": snap_anchor, "text": norm(hashing.plain(doc, s5)),
"fidelity": "F3", "result": "current"}],
"claims": [{"ref": "amber/k-bond", "pin": "illustrative-pin", "level": "illustrative",
"typst": "K_r"}],
"datasets": [{"id": "ds-hale-ctct-scan", "hash": ds_hash}]})
draft = Names.registry["smithlab2027charges"]
dd = Doc(DRAFT, [json.loads(x) for x in open(os.path.join(out, "fixtures", "packages", DRAFT, "nodes.jsonl"))])
lines = []
for nid in dd.preorder():
if dd.nodes[nid]["kind"] == "sentence":
sec = next((a for a in reversed(dd.ancestors(nid)) if dd.nodes[a]["kind"] == "section"), None)
lines.append({"id": nid, "text_hash": dd.nodes[nid]["text_hash"], "section": sec,
"ord": dd.nodes[nid]["ord"]})
lines[-1]["pinned"] = "energy-sentence"
wr(out, "draft-ids.jsonl", jsonl(lines))
# --- invalid packages ---------------------------------------------------------------
def invalid(out, tiny):
base = os.path.join(out, "fixtures", "invalid")
files = tiny["files"]
nodes = [json.loads(x) for x in files["nodes.jsonl"].decode().splitlines()]
nm = tiny["names"].ids
def edit_nodes(fn):
ns = copy.deepcopy(nodes)
fn(ns)
return jsonl(ns)
def by(ns, name):
return next(n for n in ns if n["id"] == nm[name])
def case(name, rule, change, note, raw=None):
f = dict(files)
f.update(change)
d = os.path.join(base, name, "package")
write(d, f)
if raw:
raw(d)
with open(os.path.join(base, name, "expect.json"), "wb") as fh:
fh.write((jcs.dumps({"rule": rule, "note": note}) + "\n").encode())
case("hash-mismatch", "doc.hash.node-hash", {"nodes.jsonl": edit_nodes(lambda ns: by(ns, "@a").update(node_hash="0" * 64))},
"A node_hash that does not recompute.")
case("text-edited", "doc.hash.text-hash", {"nodes.jsonl": edit_nodes(
lambda ns: by(ns, "@a").update(content=[{"kind": "text", "text": "The value is 42.5 at 300 K."}]))},
"Text changed without its hashes.")
case("unknown-kind", "package.schema", {"nodes.jsonl": edit_nodes(lambda ns: by(ns, "@p2").update(kind="aside"))},
"Kinds outside the list are refused.")
case("heading-not-first", "doc.tree.child", {"nodes.jsonl": edit_nodes(lambda ns: _swap_first(ns, nm["@sec"]))},
"A heading must be a section's first child.")
case("cite-not-reference", "doc.run.cite", {"nodes.jsonl": edit_nodes(
lambda ns: by(ns, "@b").update(content=[r if r["kind"] != "cite" else dict(r, refs=[nm["@a"]])
for r in by(ns, "@b")["content"]]))},
"A cite run names a node that is not a reference.")
case("unlisted-file", "package.manifest.unlisted", {}, "A file the manifest does not list.",
raw=lambda d: open(os.path.join(d, "notes.txt"), "w").write("stray\n"))
case("manifest-hash", "package.manifest.hash", {}, "A file changed after the manifest was written.",
raw=lambda d: open(os.path.join(d, "provenance.json"), "a").write(" "))
case("not-canonical", "package.file.canonical", {}, "A line that is valid JSON but not canonical JSON.",
raw=lambda d: _rewrite_noncanonical(d))
case("root-mismatch", "doc.hash.root", {"document.json": _doc_edit(files, root="1" * 64)},
"document.json's root is not the root node's node_hash.")
case("receipt-root", "doc.receipt.root", {"receipts.jsonl": _receipt_edit(files, root="2" * 64)},
"A receipt whose Merkle root does not recompute.")
case("receipt-signature", "doc.receipt.signature", {"receipts.jsonl": _receipt_edit(files, at="2026-10-11T12:00:01Z")},
"A receipt changed after it was signed.")
case("checks-only-plain", "doc.policy.withheld", {"document.json": _doc_edit(files, policy="checks-only")},
"A checks-only package that still carries text and plain hashes.")
case("bad-id", "package.schema", {"nodes.jsonl": edit_nodes(lambda ns: _rename(ns, nm["@d"], "i1lo0uu"))},
"IDs use Crockford base32, which has no i, l, o or u.")
case("empty-optional-file", "package.file.empty", {"suspects.jsonl": b""}, "An optional JSONL file with no records.")
case("alias-dangling", "doc.id.successor", {"ids.jsonl": jsonl([{"id": "b-p01-text-001", "kind": "alias",
"successor": "zzzzzzz"}])},
"An alias whose node does not exist.")
case("location-unknown-page", "doc.location.page", {"locations.jsonl": jsonl([
{"node": nm["@a"], "page_fp": "3" * 64, "page": 1, "boxes": [[72, 72, 200, 84]]}])},
"A location on a page no witness has.")
case("style-sup-sub", "doc.run.style", {"nodes.jsonl": edit_nodes(lambda ns: by(ns, "@c").update(
content=[{"kind": "text", "text": "A second paragraph.", "style": ["sub", "sup"]}]))},
"A run cannot be both a subscript and a superscript.")
# Zipped packages that break spec/12 §12.2: the tiny package's files plus one bad entry.
tiny_dir = os.path.join(out, "fixtures", "packages", tiny["key"])
entries = []
for rel in sorted(os.path.relpath(os.path.join(d, f), tiny_dir).replace(os.sep, "/")
for d, _, fs in os.walk(tiny_dir) for f in fs):
with open(os.path.join(tiny_dir, rel), "rb") as fh:
entries.append((tiny["key"] + "/" + rel, fh.read(), 0o644))
def zip_case(name, rule, note, extra):
d = os.path.join(base, name)
os.makedirs(d)
with zipfile.ZipFile(os.path.join(d, "package" + ZIP_EXTENSION), "w") as zf:
for ename, data, mode in entries + extra:
zi = zipfile.ZipInfo(ename, date_time=(1980, 1, 1, 0, 0, 0))
zi.compress_type = zipfile.ZIP_STORED
zi.create_system = 3
zi.external_attr = mode << 16
zf.writestr(zi, data)
with open(os.path.join(d, "expect.json"), "wb") as fh:
fh.write((jcs.dumps({"rule": rule, "note": note}) + "\n").encode())
zip_case("zip-traversal", "package.zip.entry", "An archive entry whose path leaves the package folder.",
[(tiny["key"] + "/../escape.txt", b"outside\n", 0o644)])
zip_case("zip-symlink", "package.zip.entry", "An archive entry that is a symbolic link.",
[(tiny["key"] + "/assets/link", b"document.json", 0o120777)])
# Rules the audit found no broken package for.
case("missing-required-file", "package.file.required", {"provenance.json": None}, "A required file is missing.")
case("counts-wrong", "doc.counts", {"document.json": _doc_edit(files, counts={"nodes": 1, "leaves": 1})},
"document.json's counts are not the number of nodes and leaves.")
case("rev-without-corrections", "doc.correction.rev", {"document.json": _doc_edit(files, rev=3)},
"document.json's rev is not the number of corrections.")
case("receipt-unsorted-nodes", "doc.receipt.nodes", {"receipts.jsonl": _receipt_edit(
files, nodes=list(reversed([json.loads(x) for x in files["receipts.jsonl"].decode().splitlines()][0]["nodes"])))},
"A receipt whose nodes are not sorted by ID.")
case("retired-live", "doc.id.retired-live", {"ids.jsonl": jsonl([
{"id": nm["@a"], "kind": "retired", "successor": None, "node_kind": "sentence", "rev": 1}])},
"An ID that is both live and retired.")
case("manifest-key", "package.manifest.key", {}, "The manifest names another key than document.json.",
raw=lambda d: _manifest_edit(d, key="other2026example-0000"))
case("unsafe-path", "package.path.unsafe", {}, "The manifest lists a path that leaves the package.",
raw=lambda d: _manifest_edit(d, files=[{"path": "../escape.txt", "sha256": "0" * 64, "size": 1}]
+ json.load(open(os.path.join(d, "MANIFEST.json")))["files"]))
case("crlf", "package.file.canonical", {}, "A file whose lines end in CR LF, which is not the canonical form.",
raw=lambda d: _crlf(d))
signer = [json.loads(x) for x in files["receipts.jsonl"].decode().splitlines()][0]
case("signer-mismatch", "doc.receipt.signature", {"receipts.jsonl": jsonl([
RC.seal(dict(signer, by="cara"), secret("ana"), "ana")])},
"A receipt whose signature was made with another key than the signer it names.")
case("history-wrong", "doc.history", {"document.json": _doc_edit(files, history="4" * 64)},
"document.json's history is not the root over the corrections.")
d0 = json.loads(files["document.json"])
wk = d0["work"]["key"]
bad = wk[:-4] + "zzzz"
case("key-suffix", "doc.key.suffix", {"document.json": _doc_edit(files, key=bad, work=dict(d0["work"], key=bad))},
"A work key whose hash part is not the hash of the work's DOI.")
case("key-work", "doc.key.work", {"document.json": _doc_edit(files, key=wk + ".si")},
"A document key whose suffix is not the version's.")
def _manifest_edit(d, **kw):
p = os.path.join(d, "MANIFEST.json")
m = json.load(open(p, encoding="utf-8"))
m.update(kw)
open(p, "w", encoding="utf-8").write(jcs.dumps(m) + "\n")
def _crlf(d):
p = os.path.join(d, "provenance.json")
data = open(p, "rb").read().replace(b"\n", b"\r\n")
open(p, "wb").write(data)
m = json.load(open(os.path.join(d, "MANIFEST.json"), encoding="utf-8"))
for f in m["files"]:
if f["path"] == "provenance.json":
f["sha256"], f["size"] = H(data), len(data)
open(os.path.join(d, "MANIFEST.json"), "w", encoding="utf-8").write(jcs.dumps(m) + "\n")
def _swap_first(ns, sec):
kids = sorted([n for n in ns if n["parent"] == sec], key=lambda n: n["ord"])
kids[0]["ord"], kids[1]["ord"] = kids[1]["ord"], kids[0]["ord"]
d = Doc("x", ns)
order = d.preorder()
ns.sort(key=lambda n: order.index(n["id"]))
def _rename(ns, old, new):
for n in ns:
if n["id"] == old:
n["id"] = new
def _doc_edit(files, **kw):
d = json.loads(files["document.json"])
d.update(kw)
return (jcs.dumps(d) + "\n").encode()
def _receipt_edit(files, **kw):
rs = [json.loads(x) for x in files["receipts.jsonl"].decode().splitlines()]
rs[0].update(kw)
return jsonl(rs)
def _rewrite_noncanonical(d):
p = os.path.join(d, "nodes.jsonl")
lines = open(p).read().splitlines()
lines[0] = json.dumps(json.loads(lines[0]), indent=None, separators=(", ", ": "))
data = ("\n".join(lines) + "\n").encode()
open(p, "wb").write(data)
m = json.load(open(os.path.join(d, "MANIFEST.json")))
for f in m["files"]:
if f["path"] == "nodes.jsonl":
f["sha256"], f["size"] = H(data), len(data)
open(os.path.join(d, "MANIFEST.json"), "w").write(jcs.dumps(m) + "\n")
# --- main -----------------------------------------------------------------------------
OUT = ROOT
def main(out):
global OUT
OUT = out
for sub in ("fixtures/packages", "fixtures/records", "fixtures/invalid", "fixtures/keys"):
p = os.path.join(out, sub)
if os.path.exists(p):
shutil.rmtree(p)
os.makedirs(p)
vec = os.path.join(out, "vectors")
os.makedirs(vec, exist_ok=True)
for f in os.listdir(vec):
if f.endswith(".jsonl"):
os.remove(os.path.join(vec, f))
for kid in KEYS:
with open(os.path.join(out, "fixtures", "keys", kid + ".ed25519.hex"), "w") as f:
f.write(secret(kid).hex() + "\n")
with open(os.path.join(out, "fixtures", "keys", "smithlab-team-secret.hex"), "w") as f:
f.write(TEAM_SECRET.hex() + "\n")
Names.registry.clear()
hale = build_hale(out)
build_checks_only(out, hale)
build_simple(out, "okafor2025open")
build_simple(out, "smithlab2027charges")
tiny = tiny_package(None)
tiny_dir = os.path.join(out, "fixtures", "packages", tiny["key"])
write(tiny_dir, tiny["files"])
# The same package zipped as spec/12 §12.2 says a producer writes a byte-identical archive.
write_zip(tiny_dir, tiny_dir + ZIP_EXTENSION)
records(out, hale)
vectors(out, hale, tiny)
invalid(out, tiny)
def cli():
import argparse
ap = argparse.ArgumentParser(
description="Build every fixture and test vector from fixtures/src and the oracle. The build is "
"deterministic, and tools/check.py rebuilds into a temporary folder to prove it.")
ap.add_argument("--out", metavar="DIR", default=ROOT,
help="build into DIR instead of rewriting fixtures/ and vectors/ in this repository")
out = os.path.abspath(ap.parse_args().out)
if out != ROOT:
for sub in ("registry", "fixtures/src"):
dst = os.path.join(out, sub)
if not os.path.exists(dst):
shutil.copytree(os.path.join(ROOT, sub), dst)
main(out)
if __name__ == "__main__":
cli()