feat: comment categories; emend, relink and resolve corrections

scienting-layer/1: an optional, fixed category on comment, caveat and
question bodies (comment, context, critique, question, correction,
feedback, resource).

Corrections: emend fixes the authors' own slip and keeps what was printed
(a run's printed), never lowering fidelity or a witness match; relink sets
or removes what a citation names; resolve sets a reference's resolution.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017R7sgDNvUGWY2zFPCq2JH6
This commit is contained in:
alexmandClaude committed 2026-10-17 06:17:10 -04:00
1 parent 4d59b29b63
commit 8b5ee87275
33 files changed
+726 -80

No files matched your search

+5 -1
View File
@@ -17,6 +17,9 @@ In this chapter *H* is SHA-256 written as hexadecimal; under a keyed scheme, *H*
| `node_hash` | *H*(canonical JSON of `{"own": own_hash, "children": [each child's node_hash, in order]}`) | Pins on leaves; the root hash; knowing that something below has changed. |
Normalization is that of [1.4](01-conventions.md#14-text-normalization-for-hashing).
The plain projection is of the runs as stored, and the strict and lossy projections of the runs as printed ([3.6](03-nodes.md#36-the-plain-projection)).
So an `emend`, which corrects the authors' own slip ([11.6](11-corrections.md#116-emendations)), changes a leaf's `text_hash`, `own_hash` and `node_hash`, because the text that is read, quoted and pinned is the emended one.
It leaves the leaf's `match_hash` and `align_key` as they were, because the witnesses they compare the leaf with hold the printed text, and a value computed from the emended text would report every emendation as a disagreement.
Because each `node_hash` covers its children's, the root's `node_hash` covers the whole tree (it is a hash over canonical JSON of `{own, children}`, not an RFC 6962 Merkle root, which [8.5](08-fidelity.md#85-merkle-roots) defines for receipts): a correction to one sentence changes that sentence's values and the `node_hash` of each ancestor, and nothing else.
`hashes.jsonl` gives every value for every node of a small document, with each node's own content.
@@ -42,7 +45,7 @@ A table's columns name header cells by ID, so fixing a header cell changes that
## 5.3 The strict projection
The strict projection folds notation to one canonical linear form and keeps structure, so that it can certify agreement.
It is computed over a node's runs, or its math, as follows; for other nodes it is the strict projections of the text leaves joined with U+0020, then step 3.
It is computed over a node's runs as printed ([3.6](03-nodes.md#36-the-plain-projection)), or its math, as follows; for other nodes it is the strict projections of the text leaves joined with U+0020, then step 3.
1. Apply NFC to every string, then project each run:
@@ -142,6 +145,7 @@ A node's prose length is the number of code points in its lossy projection other
Two readings of a document are aligned as whole sentence sequences, never paragraph by paragraph, because backends disagree about paragraphs at page and column breaks.
Each reading is a list of sentences in document order, each with its strict projection, lossy projection, complex keys, container and context (the kind of its container: paragraph, heading, caption and so on).
Those projections are of the runs as printed ([5.3](#53-the-strict-projection)), so a corrected document aligned with a witness again shows no emendation as a suspect.
1. **Anchors.** A sentence is a candidate when its lossy projection occurs exactly once in each reading and its prose length is at least 20.
Take the candidates in the order of the first reading, and choose the longest subsequence whose positions in the second reading increase; among equally long ones, the one whose sequence of positions in the first reading is lexicographically smallest.