- Add tools/validate.py: validates packages, and with --correction runs proposed corrections through the gate against a package without writing anything (spec/11 §11.4). The gate now refuses a proposal that fails the correction schema, rule correction.schema. - tools/load.py validates every package before loading, and with --bundle --built-from builds a scienting-bundle/1 with the FTS5 search index and bundle_meta (spec/13 §13.5). Search rows are defined per unit (sentence, cell, equation outside a sentence, figure-text) with the parent kind as context. - Roster keys carry a role, and a key with the role owner may revoke any receipt (spec/08 §8.7, spec/20); roster schema, store table and the fixture roster updated. The checker signs a trial revocation to prove the rule both ways. - A qa: receipt raises the nodes it sampled except those in its errors (spec/08 §8.2); the example paper's fidelity counts become 27 F3, 13 F2, 4 F1 and 1 F0. - The destination chooses the keyed scheme, not the text policy: hmac-team between the team's machines, hkdf-pages outside the team (spec/05 §5.8, spec/12 §12.5). - A TextQuoteSelector must repeat the anchor's plain quote and is absent for hashed or keyed anchors (spec/18 §18.3). - Add fixtures/records/suspect.json, a scienting-suspect/1 record. - Oracle: hashes are kept incrementally and checked after every correction against a full recomputation; load and write refuse unsafe manifest paths; Ed25519, HKDF and schema validation come from the cryptography and jsonschema packages; canonical JSON uses the built-in encoder with an exact RFC 8785 fallback. - The tools need Python 3.14 with cryptography and jsonschema; the py310 CI environment is dropped and the workflow runs the default only. - Resolve every [CHECK] note in the docs, READMEs and context; add CONTRIBUTING.md; every tool answers --help and refuses unknown options. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
6.1 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. |
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:
- it does not match the correction schema,
schemas/correction.schema.json; - a target, parent or sibling is not live, or the root would be deleted, moved, merged or reclassified;
findis empty or does not occur exactly once;- an
inserthas no crop evidence, or names a run or offset its target does not have; - a digit changes without crop evidence: in a
replacewhosefindand replacement have different digits, adeleteof leaves that hold digits, or arelabelwhose labels have different digits; - any leaf outside
touchedhas different digits afterwards; - a
splitfalls outside the sentence, inside a run other than text, or leaves a part empty; - a
mergejoins nodes that differ in kind, are not mergeable, or are not adjacent siblings; - a
movewould put a node inside itself; - a
reclassifychanges the content shape, or arelabelorsettargets a kind that does not take it; or - 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.
tools/validate.py --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.