Files
paperspec/spec/11-corrections.md
T
alexmandClaude 607062056f feat!: pin Python 3.14 and Unicode 16.0, and make the tools commands
Python and Unicode
- requires-python is >=3.14,<3.15: 3.14 is the only Python with the
  Unicode 16.0 data the specification pins (3.13 has 15.1, 3.15 has 17.0).
- `import paperspec` raises ImportError unless unicodedata reports
  paperspec.UNICODE_VERSION (16.0.0), so a source checkout or an install
  that bypasses requires-python cannot compute values on other data.

Commands
- tools/{check,build,validate,load,stress,site}.py move with history to
  src/paperspec/cli/ and install as paperspec-check, -build, -validate,
  -load, -stress and -site; each also runs as `python -m paperspec.cli.X`.
- paperspec.cli.require() exits with "install paperspec[tools]" when
  cryptography or jsonschema is missing; paperspec.cli.checkout() finds
  the repository the check, build, stress and site commands work on (the
  editable install's own checkout, else the one around the current
  folder) and exits with a message outside one. tools/__init__.py is gone.
- validate and load read the schemas and SQL through the new
  paperspec.resources.path(), which finds them in a wheel and in an
  editable install alike, so they work anywhere the package is installed.
- check runs the rebuild as `python -m paperspec.cli.build`.
- build still writes the provenance tool label "paperspec tools/build.py":
  it is part of every hashed and signed fixture.
- pixi tasks and the check workflow run the commands; tools/README.md
  documents them, and the other docs name the commands instead of paths.

License
- LICENSE.md gives `src/` Apache-2.0 alongside `tools/`. Since the
  previous commit moved the code to src/, no row had covered it.

pixi run check passes (run as `python -m paperspec.cli.check` with src/ on
the path, since this environment cannot install the entry points); the
byte-for-byte rebuild matches, so no fixture or vector changed.
pixi.lock still needs `pixi lock`.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-15 09:17:27 -04:00

7.3 KiB

11. Corrections

A correction, scienting-correction/1, is an operation on an established document, with its evidence and its author. Corrections are added and never edited, so a document's history is its original reading plus its corrections. Each has an index, counted from 1 in the order corrections are applied, and the document's revision is the index of the last.

11.1 Operations

op Fields Does IDs
replace target, find, replace, optional run Replaces the one occurrence of find among the target's editable strings with replace, a string or, inside a text run, a list of runs. Unchanged.
insert parent, place_after (a sibling, or null for first), node Adds a subtree: kinds, labels, attributes and content, with each equation run naming its child as {"kind": "equation", "child": i}. New IDs, minted (4.2).
insert target, run, offset, text Inserts text into one run of the target, at a code-point offset in that run's editable string. Unchanged.
delete target Removes an artifact and its subtree, such as a running head read as text. All retired, without successors.
split target, offset Splits a sentence at an offset in its plain projection, which falls between runs or inside a text run; whitespace at the split is removed from both parts, and each equation child goes with its run. The first part keeps the ID; the second gets a new one.
merge target, with, optional joiner Joins a node with its next sibling of the same kind: the runs of two run-bearing nodes, with the joiner (default one space) between, or the children of two paragraph, list, item, quote, caption, figure-note or table-note nodes. with is retired, with the target as successor.
move target, parent, place_after Changes reading order or containment. Unchanged.
reclassify target, kind Changes a node's kind, such as a paragraph that is really a caption, to a kind with the same content shape. Unchanged.
relabel target, label Changes a printed label. Unchanged.
set target, attr, value Sets one structural attribute: a section's level, a table's columns, a cell's column, rowspan, colspan or role, a row's header_path, a list's ordered, an item's marker or a theorem's variant. Unchanged.

A leaf's editable strings are the text of its text, link, cite, xref and fnmark runs and the source of its math runs, each searched on its own; an equation's source; a verbatim node's text. set exists because readings drop spans, roles and header paths; see the rationale. Runs are made canonical after every operation, and the result MUST satisfy every rule of 3.

11.2 Every correction records

Property Holds
evidence At least one item: crop, witness or source-file by SHA-256, or person.
author {key} for a person in the roster, or {model, run} for a model's session, where model is {name, provider?, version?, weights?, prompt?, parameters?, processor?}: the model's name, who served it, its version, and SHA-256 of the weights, the instructions and the sampling parameters it ran with, as far as the team can obtain them, and where it ran (14).
reason, at Why, and when.

A proposal, such as one a tool makes, carries these and status: "proposed", and no applied fields.

11.3 Applied fields

When a correction is applied, its record gains:

Property Holds
index, id Its position, and its record ID.
before, after The target's node_hash before and after, or null; for an inserted subtree, target is its root.
path The target's ancestors when it was applied, root first; for an insert, the new subtree's.
to_path For a move, the new ancestors.
touched The leaves whose content the correction changed, sorted: the target of a replace, text insert, relabel or set on a leaf; the new leaves of a node insert; the deleted leaves of a delete; both parts of a split; both nodes of a run merge, or the moved-in leaves of a container merge; the moved leaves of a move; the leaves of a reclassified node.
created, retired IDs minted or retired.
from The label, kind or attribute value before a relabel, reclassify or set.

Retirements are also entered in ids.jsonl with the correction's index as rev. Drift (6.6) is computed from path, to_path and touched alone, so it needs no replay.

11.4 The gate

A correction is refused, naming the rule (22), when:

  1. it does not match the correction schema, schemas/correction.schema.json;
  2. a target, parent or sibling is not live, or the root would be deleted, moved, merged or reclassified;
  3. find is empty or does not occur exactly once;
  4. an insert has no crop evidence, or names a run or offset its target does not have;
  5. a digit changes without crop evidence: in a replace whose find and replacement have different digits, a delete of leaves that hold digits, or a relabel whose labels have different digits;
  6. any leaf outside touched has different digits afterwards;
  7. a split falls outside the sentence, inside a run other than text, or leaves a part empty;
  8. a merge joins nodes that differ in kind, are not mergeable, or are not adjacent siblings;
  9. a move would put a node inside itself;
  10. a reclassify changes the content shape, or a relabel or set targets a kind that does not take it; or
  11. the result breaks a rule of 3.

The gate needs only the document's current state, so a tool can run it on a proposal before applying anything, and get back the record the correction would append or the rule it breaks. paperspec-validate --correction does this against a package.

Digits are characters of General Category Nd in the plain projection. A package's corrections are also checked as a whole: indexes run from 1 without gaps, each id derives from its record, rev equals their number, every retired ID is in ids.jsonl, and the last correction of each leaf ends at its current node_hash. corrections.jsonl in the vectors applies each operation, and each refusal, to a small document.

11.5 The history root

document.json carries history: the root of the tree of 8.5 whose items are ["correction", index, id] for each correction in order, or SHA-256 of the empty string when there are none, which is how RFC 9162 hashes the empty tree. Because corrections are only appended, a consistency proof (RFC 9162 §2.1.4) between the roots at two revisions shows that the later history extends the earlier one rather than rewriting it. The proof is a scienting-proof/1 with kind consistency, holding the doc, from_rev, to_rev, from_root, to_root and the path as hex hashes; a verifier applies RFC 9162 §2.1.4.2. A checks-only package carries no corrections, so its history is the empty root. proofs.jsonl gives consistency proofs for every prefix of small trees and for the example paper between revisions 8 and 12; fixtures/records/proof-consistency.json is the latter.