Files
paperspec/sql
alexmandClaude ea0a7efa78 feat: zipped packages, target set, checklists, audit fixes and a stress test
Features for the next six months (accessible HTML export, paperpack and
Lode on packages, the personal bundle):

- Zipped packages: the oracle writes and reads `.sct.zip` archives under
  the rules of spec/12 §12.2; validate.py and load.py accept them; a
  byte-identical zip of the tiny package joins the fixtures, and two
  broken archives (traversal, symbolic link) join fixtures/invalid.
- The target set of targeted F3 (spec/08 §8.10) is computed by the
  oracle and pinned by vectors/targets.jsonl under each digit rule; the
  rule now covers cells as well as sentences and the leaves of anchored
  containers, and defines a node's section.
- scienting-checklist/1 (provisional): a conformance check of an export,
  covered by a receipt as ["checklist", id, hash] (spec/08 §8.11), with a
  WCAG 2.1 AA fixture and its receipt.
- QA reports are signed like receipts (spec/08 §8.9).
- A `description` annotation body with role alt or long, so an
  accessible export can find a confirmed figure description; the layer
  fixture carries one, and the store's body-kind constraint allows it.
- scienting-spot-check/1, the seed payload, is registered with a schema.
- exercise, submission and grade are provisional until a course has
  used them.

Audit fixes, each with a vector or a broken package where one was
missing:

- Folder packages: symbolic links are refused on validate and load,
  manifest paths must not collide under case folding, listed files are
  matched by exact name, files are hashed in chunks under a size limit.
- Files are compared byte for byte, so CR LF, a byte order mark, NaN,
  a lone surrogate or invalid UTF-8 is refused instead of translated;
  integers beyond 2^53 are read as doubles, as canonical JSON writes them.
- Receipts: signature.key must equal by and the key must be valid when
  signed; a qa receipt's nodes must be the sample at the original's
  hashes, its scope must name qa.of, its errors must be sampled nodes;
  statuses are computed once, iteratively, so chains of spot-checks
  hundreds deep settle; Base64 and manifest shape errors become refusals
  instead of tracebacks; counts and the fidelity of roster-less packages
  are recomputed.
- The gate enforces rule 11 of spec/11 §11.4: changed nodes are
  validated against the node schema and the references between nodes
  are checked, so a set to a bad value, an insert with unknown
  properties or a delete of a referenced header cell is refused; a
  relabel to null drops the label; a reclassify drops foreign
  attributes; a split strips whitespace across runs; ids.jsonl order
  follows spec/04 §4.4 in every state; a move after itself is refused.
- Anchors: the relabel rule requires that nothing else changed below the
  node; an alias chain ending in a deletion no longer crashes resolve.
- Notation: a script after an empty group applies to the previous atom
  ({}^{14}C); letters are defined by Unicode block in spec/05 §5.4 and
  the oracle; \left) is complex.
- Page fingerprints delete whitespace before folding, so "10 -20" and
  "10-20" fold alike (spec/07 §7.1).
- Grades are summed with exact decimals; the reviewer error bound is
  computed with integer arithmetic.
- receipts and revocations are keyed by (doc_key, id) in SQL, since one
  revocation may be carried by several packages.

Checking:

- tools/stress.py: seeded randomized property tests over canonical JSON,
  normalization, notation, random correction walks, anchor resolution,
  Merkle roots, spot-check samples, error bounds, fingerprints, mutated
  archives and packages, rights and spot-check chains; CI runs it.
- check.py verifies every count and version the prose states against
  the repository, the zipped fixture and crafted archives, the checklist
  coverage, and nine more broken packages.
- CONTRIBUTING.md gains a release checklist.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-10-14 20:07:22 -04:00
..

SQL

This folder holds the table definitions for the database form of packages, a team's store, and the bundle.

What the database form is for

A package is a folder of canonical JSON files, one document per folder (chapter 12). Canonical JSON is JSON with exactly one valid byte sequence, so a file's hash changes only when its content does.

Handing over a folder is easy. Querying across hundreds of documents in folders is hard. The database form holds the same data as rows so that a team can ask SQL questions of it, and a package loaded into the tables unloads back to the same bytes. About 500 documents come to about a million and a half rows, and SQLite and Dolt hold that comfortably (chapter 13).

The three files

File Holds Runs on
doc.sql Documents: works, documents, witnesses, nodes, pages, locations, references, citations, corrections, IDs, receipts, revocations, suspects and package files. SQLite, Dolt, PostgreSQL
store.sql What a team adds: roster keys, projects, members, reading paths, layers, annotations, datasets, digitizations, views, policies, submissions and grades. Load doc.sql first. SQLite, Dolt, PostgreSQL
bundle-sqlite.sql The bundle's extra tables: metadata, a full-text index (FTS5, SQLite's search extension) and room for embeddings. Load both files above first. SQLite

Creating a database

sqlite3 lab.db < sql/doc.sql && sqlite3 lab.db < sql/store.sql
psql -d lab -f sql/doc.sql && psql -d lab -f sql/store.sql
dolt sql < sql/doc.sql && dolt sql < sql/store.sql

The PostgreSQL database lab and the Dolt repository must already exist. The sqlite3 line runs as shown; the psql and dolt lines follow the portability rules of chapter 13 but have not been run on those engines in this repository.

To load packages into SQLite without pixi, run python3 tools/load.py --sqlite lab.db PACKAGE… with Python 3.14 and two packages, jsonschema and cryptography installed. The file docs/examples.md has queries to try. The loader copies files that have no table (assets, dataset files, derived files) by SHA-256 into lab.db.assets/.

A first query

The query below finds every sentence that cites a given reference, which is the citation's context. It returns sentence IDs, and nodes.content holds each sentence's text.

SELECT c.sentence
FROM citations c
JOIN refs r ON r.doc_key = c.doc_key AND r.node = c.ref_node
WHERE c.doc_key = 'hale2026harmonic' AND r.label = '2';

On fixtures/packages/hale2026harmonic it returns one row, f929eq9: the sentence ending "served as the first test case, with a bending band near 1595 cm⁻¹."

Conventions

  • Foreign keys: none. Validators check references, so the same file runs everywhere.
  • Types: enumerations are VARCHAR with a CHECK, JSON values are canonical JSON in TEXT, and booleans are INTEGER 0 or 1.
  • Names: no table, column or index name is reserved in SQLite, PostgreSQL or MySQL (Dolt is MySQL-compatible). The formats' key, index, at, by, before and after become doc_key, idx, made_at, signer, before_hash and after_hash.
  • Round trip: each row keeps every property of its record, so unloading restores the same bytes and the same manifest hash. Unloading copies the files with no table from the asset store, which must travel with the database. tools/check.py tests this for every fixture package.
  • Derived tables: citations, receipt_nodes and annotation_targets are built on load for queries and are not part of the package.

Status

The version is 0.1.0. In registry/formats.json, scienting-doc/1 is candidate, and the bundle and the project, dataset, digitization and view formats are provisional. Chapter 13 defines the mapping from files to tables.