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>
This commit is contained in:
142 files changed
+2312
-268
No files matched your search
@@ -56,7 +56,7 @@ The specification as a whole is versioned with semantic versioning; this is vers
|
||||
## 0.4 Status
|
||||
|
||||
Each format has one of three statuses, listed in [registry/formats.json](../registry/formats.json).
|
||||
In version 0.1.0 the registry lists 27 formats: 20 are `candidate` and 7 are `provisional`; none is `reserved`.
|
||||
In version 0.1.0 the registry lists 29 formats: 18 are `candidate` and 11 are `provisional`; none is `reserved`.
|
||||
|
||||
| Status | Meaning |
|
||||
| --- | --- |
|
||||
|
||||
+2
-2
@@ -70,7 +70,7 @@ A later minor version may add stage 2, which replaces U+FFFC with canonical Math
|
||||
|
||||
A character is simple, after folding, when it is:
|
||||
|
||||
1. a letter: General Category L and Script Latin or Greek, or U+00B5 MICRO SIGN;
|
||||
1. a letter: General Category L in the Latin blocks U+0041–U+024F and U+1E00–U+1EFF, or in the Greek blocks U+0370–U+03FF, without the Coptic letters U+03E2–U+03EF, and U+1F00–U+1FFF; or U+00B5 MICRO SIGN;
|
||||
2. a digit: General Category Nd; or
|
||||
3. one of `+ − = × ⋅ / < > ≤ ≥ ± ( ) ′ . ,`.
|
||||
|
||||
@@ -86,7 +86,7 @@ There are no spaces in the form, and a script with no base attaches to the chara
|
||||
|
||||
| Token | Meaning |
|
||||
| --- | --- |
|
||||
| `{` … `}` | A group; its atoms are spliced in. A script on a group of more than one atom makes the source complex. |
|
||||
| `{` … `}` | A group; its atoms are spliced in. A script on a group of more than one atom makes the source complex; a script after an empty group applies to the atom before it, or to an empty base, so `{}^{14}C` is `^{14}C`. |
|
||||
| `_`, `^` | A subscript or superscript of the previous atom (or of an empty base), taking one argument: a group, a command or a character. A second script of the same kind is complex. |
|
||||
| `'` | A prime on the previous atom. |
|
||||
| `\mathrm`, `\text`, `\textrm`, `\mathup` | A wrapper: its argument's atoms are spliced in. |
|
||||
|
||||
+3
-3
@@ -11,12 +11,12 @@ To compute the fingerprint:
|
||||
|
||||
1. Remove every line that a stamp rule matches ([7.2](#72-stamp-rules)).
|
||||
2. Join the remaining lines with U+0020.
|
||||
3. Fold Unicode script characters and dashes before digits as the strict projection does for text ([5.4](05-hashes.md#54-simple-notation)), then apply NFKC.
|
||||
4. Delete every whitespace character and every character of General Category Cc or Cf.
|
||||
3. Delete every whitespace character and every character of General Category Cc or Cf.
|
||||
4. Fold Unicode script characters and dashes before digits as the strict projection does for text ([5.4](05-hashes.md#54-simple-notation)), then apply NFKC.
|
||||
5. Round the width and height to whole points, half up.
|
||||
6. The fingerprint is SHA-256 of the canonical JSON of `{"format": "scienting-page/1", "size": [width, height], "text": the text, "images": the image hashes, sorted}`.
|
||||
|
||||
Whitespace is deleted rather than collapsed because text extractors disagree about where they insert spaces.
|
||||
Whitespace is deleted rather than collapsed because text extractors disagree about where they insert spaces, and it is deleted before folding so that "10 -20" and "10-20" fold alike.
|
||||
A scanned page with no text layer is fingerprinted by its size and its images alone.
|
||||
Two witnesses whose fingerprints are equal, page for page, are the same edition of the paper.
|
||||
|
||||
|
||||
+26
-4
@@ -85,6 +85,7 @@ A receipt is:
|
||||
|
||||
Only valid receipts set levels.
|
||||
A suspended receipt stays suspended; its reviewer reviews the sitting again and signs a new receipt, which needs its own spot-check.
|
||||
A spot-check is itself a receipt, so its status may depend on its own spot-check; statuses are settled from the end of each chain, and the receipts of a chain that loops back on itself are invalid.
|
||||
|
||||
## 8.7 Revocations and the roster
|
||||
|
||||
@@ -108,8 +109,9 @@ A seed the reviewer controls could be ground, by varying the receipt until the s
|
||||
3. Rank the original's node IDs by the hexadecimal SHA-256 of the seed's 64 bytes followed by the ID's UTF-8, then by ID, and take the first ones.
|
||||
|
||||
The `qa` object records `of`, `key`, `seed` (Base64), `rate`, `minimum`, the `sample` sorted by ID, and the `errors` found, each `{node, kind?, note?}`.
|
||||
The `qa:` receipt's `nodes` are the sampled nodes at the hashes the original lists.
|
||||
The `qa:` receipt's `nodes` are the sampled nodes at the hashes the original lists, its `scope` is `qa:` and the original's ID, every error names a sampled node, and it is signed at or after the original.
|
||||
Anyone can recompute the sample; `spotcheck.jsonl` does.
|
||||
The payload's identifier, `scienting-spot-check/1`, is registered with the other formats so that no file ever carries it by mistake; it names a signing payload, never a file.
|
||||
|
||||
## 8.9 Reviewer error bounds
|
||||
|
||||
@@ -119,10 +121,30 @@ It is reported to six decimal places, rounded up.
|
||||
With no errors the bound is about 3/*n*, so zero errors in 300 sampled nodes bounds a rate below 1%; `bounds.jsonl` gives cases.
|
||||
|
||||
A QA report, `scienting-qa-report/1`, states for each reviewer who signed a delivery the nodes sampled, the errors found, the confidence, the method `clopper-pearson` and the bound.
|
||||
It is the record a buyer keeps, so it is signed like a receipt ([8.4](#84-record-hashes-and-signatures)): `by` names the roster key of the team that made the delivery, `id` derives from the record hash, and `signature.key` equals `by`.
|
||||
|
||||
## 8.10 Targeted F3
|
||||
|
||||
The target set of a document is every node that an extract, a dataset value, a confirmed `claim-link`, a draft quotation or a pinned citation anchors, plus every display equation, plus every sentence with a digit in a `text` or `math` run.
|
||||
Digits in `cite`, `xref` and `fnmark` runs do not count.
|
||||
A project sets the digit rule to `all`, `anchored-sections` (only sentences in a section that holds an anchored node) or `none`.
|
||||
Most of a paper is never relied on, so a team proofreads the target set: the nodes its claims, data and quotations rest on.
|
||||
|
||||
1. The anchored nodes: every live node that an extract, a dataset value, a confirmed `claim-link`, a draft quotation or a pinned citation anchors, each anchor resolved first ([6.5](06-anchors.md#65-resolution)).
|
||||
An anchored leaf is a target; an anchored container contributes its leaves.
|
||||
2. Every `equation` and `equation-line`.
|
||||
3. Under the project's digit rule ([20](20-projects.md)): with `all`, every `sentence` and `cell` with a digit in a `text` or `math` run; with `anchored-sections`, only those in a section that holds an anchored node; with `none`, none.
|
||||
Digits are characters of General Category Nd.
|
||||
Digits in `cite`, `xref` and `fnmark` runs do not count, so citation marks, "Figure 3" and footnote marks pull nothing in.
|
||||
A node's section is its nearest ancestor-or-self of kind `section`, or the `front` or `back` matter when it has none.
|
||||
|
||||
The set is reported in document order, each node with the first reason that applies: `anchored`, `equation` or `digits`.
|
||||
Neighbors shown for context are not in the set, and nothing new comes to rest on a node below F3 without the node joining the set.
|
||||
`targets.jsonl` gives the set of `fixtures/packages/hale2026harmonic` under each digit rule.
|
||||
|
||||
## 8.11 Checklists
|
||||
|
||||
An export of a document, such as accessible HTML, can be checked against a standard, and that check is a record a buyer keeps.
|
||||
A checklist, `scienting-checklist/1`, holds the `doc` and `rev` checked; the `standard`, `{name, level?, url?}`, such as WCAG 2.1 at level AA; the `export` checked, `{kind, sha256, size?}`, so the record binds to one file; its `items`, each a `criterion`, its `result` (`pass`, `fail` or `not-applicable`), the `method` (`manual`, or `automated` with the `tool` and its version) and an optional `note`; and `by` and `at`.
|
||||
Its content hash is SHA-256 of the canonical JSON of the record without `id`, and its `id` is the record ID of that hash ([4.6](04-identity.md#46-record-ids)).
|
||||
A checklist carries no signature of its own: the person who checked signs a receipt that lists it as `["checklist", id, hash]` ([8.3](#83-receipts)), so one sitting covers a checklist together with the nodes read for it.
|
||||
The receipt covers the checklist only at that hash, so a checklist changed afterwards is uncovered.
|
||||
`fixtures/records/checklist/` holds a checklist for an accessible HTML export of the example paper and the receipt that covers it.
|
||||
The format is `provisional` ([0.4](00-introduction.md#04-status)).
|
||||
+3
-1
@@ -29,8 +29,10 @@ Every file is in the canonical form of [1.9](01-conventions.md#19-files).
|
||||
## 12.2 Paths
|
||||
|
||||
Paths use `/`, are relative, have no empty, `.` or `..` segment, no backslash and no NUL, and are already normalized.
|
||||
No two paths are the same once case-folded, so a package reads alike on every file system.
|
||||
A package folder carries no symbolic links, and a consumer refuses one that does, as it refuses a package whose files exceed its own size limit.
|
||||
In a zip, entries are UTF-8 named, sit under one top-level folder named by the key, are stored or deflated, carry no symbolic links, and are unique; a consumer refuses an archive that breaks any of these, or whose expanded size exceeds its own limit.
|
||||
A producer that wants byte-identical archives writes entries sorted by path, dated 1980-01-01 00:00, with no extra fields.
|
||||
A producer that wants byte-identical archives writes entries sorted by path, dated 1980-01-01 00:00, with no extra fields; `fixtures/packages/tiny2026example.sct.zip` is such an archive.
|
||||
|
||||
## 12.3 The manifest
|
||||
|
||||
|
||||
+4
-1
@@ -53,8 +53,11 @@ Replies are annotations that target annotations, so discussions are threads anch
|
||||
| `prerequisite` | `SpecificResource` | `source` | What to read or know first. |
|
||||
| `crosslink` | `SpecificResource` | `source`, `selector` | That the span corresponds to a span in another document. |
|
||||
| `translation` | `TextualBody` | `language` | The span in another language. |
|
||||
| `description` | `TextualBody` | `role` | A description of a figure, panel or table for readers who cannot see it: `alt`, a short text equivalent, or `long`, a full description. |
|
||||
| `generated` | `TextualBody` | `generator` | Text a model wrote about the span; never mistaken for source. |
|
||||
|
||||
A textual body has a `value`, and optionally a `format` (`text/plain` or `text/markdown`) and a `language`.
|
||||
Nothing a model writes is ever stored as source text: figure descriptions and drafted alt text are `generated` annotations.
|
||||
Nothing a model writes is ever stored as source text.
|
||||
A model may propose a `description`, and like any annotation it stays `proposed` until a person confirms it; an accessible export uses confirmed descriptions only, and a figure with none has no description.
|
||||
Other text a model writes about a span, such as a summary, is a `generated` annotation, which names its `generator`.
|
||||
`fixtures/records/layer/` holds a layer with one annotation of each common kind and the receipt that confirms some of them.
|
||||
@@ -1,6 +1,7 @@
|
||||
# 19. Exercises, submissions and grades
|
||||
|
||||
Because every sentence and cell has a permanent address, the answer to a verification exercise is an address, and an address can be graded.
|
||||
The three formats of this chapter, exercise, submission and grade, are `provisional` ([0.4](00-introduction.md#04-status)): no course has used them yet, and the first that does will shape them.
|
||||
|
||||
## 19.1 Exercises
|
||||
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
| Path | Holds | A conforming implementation |
|
||||
| --- | --- | --- |
|
||||
| `vectors/*.jsonl` | Inputs and expected outputs, one case per line. | Computes every expected output from its input. |
|
||||
| `fixtures/packages/` | Valid packages: a converted chemistry paper with corrections, receipts, a multi-level-header table and a dataset; its checks-only export; an open-access paper with two witnesses; a Typst draft; a tiny document. | Accepts each, and recomputes every value it contains. |
|
||||
| `fixtures/packages/` | Valid packages: a converted chemistry paper with corrections, receipts, a multi-level-header table and a dataset; its checks-only export; an open-access paper with two witnesses; a Typst draft; a tiny document, as a folder and as the zip of [12.2](12-package.md#122-paths). | Accepts each, and recomputes every value it contains. |
|
||||
| `fixtures/records/` | A valid record of every other format that is a JSON file of its own; the stage requirements and stamp rules are in `registry/`, and a bundle is built by `tools/load.py --bundle`. | Accepts each. |
|
||||
| `fixtures/invalid/` | Invalid packages, each with `expect.json` naming the rule it breaks. | Refuses each, and SHOULD name that rule. |
|
||||
| `fixtures/keys/` | The test keys that signed the fixtures and the team secret of the checks-only export. | Never uses them for anything else. |
|
||||
@@ -17,9 +17,9 @@ Each part's CI runs this suite against its own implementation, so the parts cann
|
||||
|
||||
A validator checks, in order, and reports each failure as `path:line: rule: message`:
|
||||
|
||||
1. The manifest: present, valid, sorted, every listed file present with its hash and size, nothing unlisted, paths safe, required files present.
|
||||
2. Every file in canonical form, and every record against its schema.
|
||||
3. The tree ([3](03-nodes.md)), runs and references between nodes.
|
||||
1. The manifest: present, valid, sorted, every listed file present with its hash and size, nothing unlisted, paths safe and no symbolic links, required files present.
|
||||
2. Every file in canonical form, byte for byte, and every record against its schema.
|
||||
3. The tree ([3](03-nodes.md)), runs and references between nodes, and the counts.
|
||||
4. The policy and hashing scheme ([12.5](12-package.md#125-text-policies)).
|
||||
5. The five values of every node, the root and `meta_hash`, when the text is present and the key, if any, is known.
|
||||
6. The ID table, the corrections, the witnesses, pages, locations and references, and every file's order.
|
||||
|
||||
Reference in new issue
Block a user