feat: trace annotation body for traced citations; paperspec.traces
A trace follows one citation of a sentence of a citing paper to a passage of the cited paper (spec/18 section 18.5). Minor change: a new body kind, optional properties and two rules, with no valid file made invalid. - annotation schema branch (source, selector, reference, note, verdict, proposer; proposer required for a Software creator); body_kind in sql/store.sql; the note and proposer terms of the JSON-LD context; rules layer.trace.source and layer.trace.target. - paperspec.traces: resolve(annotation, citing_state, cited_state, same_as) gives current, changed, stale, unlinked or misresolved with open and cites; problems, disputes, disputed. - fixtures/records/traces/ on writer2027fixed-ynr7.v3 and vectors/traces.jsonl (29 cases); check.py runs the record checks over both layers; counts, glossary, examples and CHANGELOG updated. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Uy4ZYbWRoCdUtWLEfigysY
This commit is contained in:
25 files changed
+966
-50
No files matched your search
@@ -87,3 +87,18 @@ The last two make some files that were valid invalid, which [spec/00](spec/00-in
|
||||
A checkpoint note was split into lines with `str.splitlines`, which also breaks at U+000B and other separators and so accepted a note whose last newline was replaced by one; [spec/23 §23.2](spec/23-log.md#232-checkpoints) now says every line ends in U+000A alone, `paperspec.log.parse_checkpoint` splits on it, and `paperspec check` tries ten other separators.
|
||||
The third was the stress harness's own: a truncation could cut a file at its full length, leaving the package unchanged and valid, so it now always shortens the file.
|
||||
So was a fourth, with 1000 rounds: the random walk of corrections could delete the body section, leaving nothing to correct and too few corrections accepted, so a delete now leaves at least eight nodes.
|
||||
|
||||
One more, asked for by paperdesk, which lets a reader follow a citation to the passage it rests on.
|
||||
It is a minor change in the sense of [spec/00](spec/00-introduction.md): a new body kind, optional properties and two rules, with no valid file made invalid, no computed value changed and no existing vector case or package changed.
|
||||
|
||||
- Traced citations ([spec/18 §18.5](spec/18-layers.md#185-traced-citations), `annotation.schema.json`, the `note` and `proposer` terms of the JSON-LD context): a `trace` body follows one citation of a sentence of a citing paper to a passage of the cited paper.
|
||||
The target is a leaf or span anchor on the citing sentence, which must cite the body's `reference`; the body names the cited document in `source`, the passage in `selector` (an `AnchorSelector` with its pin and span, then a `TextQuoteSelector` when the quote is plain), and optionally a `note`, a `verdict` and a `proposer` (`{model, run, score?, rank?}`, required when the creator is `Software`).
|
||||
A `crosslink` stays what it was, a symmetric correspondence of two spans with no room for a note, the reference meant or a proposer's provenance.
|
||||
The state of a trace is the annotation's `status`, and a trace is disputed while an `assessing` reply to it stands, which is computed from the replies.
|
||||
The `body_kind` CHECK of the store's `annotations` table takes `trace` ([sql/store.sql](sql/store.sql)), and `paperspec check` holds it to the schema's list of body kinds.
|
||||
- `paperspec.traces` and the rules `layer.trace.source` and `layer.trace.target` ([spec/22 §22.4](spec/22-conformance.md#224-rules)).
|
||||
`resolve(annotation, citing_state, cited_state, same_as)` resolves both anchors by [spec/06 §6.5](spec/06-anchors.md#65-resolution) and returns `current`, `changed`, `stale` (with the side), `unlinked` (a `relink` removed the citation) or `misresolved` (a `resolve` points the reference at another work), with `open`, the passage's current address, and `cites`, the citations inside it, so a chain of citations is followed one trace at a time.
|
||||
`problems` reports the two rules, and `disputes` and `disputed` read the replies.
|
||||
- `fixtures/records/traces/` is a second layer, on the Typst draft, with a model's confirmed trace to the main example's abstract and its receipt, a rejected candidate, a person's trace to Table 1 and a reply that disputes the first, and `vectors/traces.jsonl` has 29 cases that correct either paper and show every outcome and both rules.
|
||||
`paperspec check` runs both layers through the record checks and the store.
|
||||
The example project holds the new layer, which changes `project.json`; no other fixture changes.
|
||||
+13
-6
@@ -57,14 +57,14 @@ This context defines `kind`, `status`, `audience`, `AnchorSelector` and `anchor`
|
||||
|
||||
## What it defines
|
||||
|
||||
The file defines 25 terms.
|
||||
Twenty-four map to IRIs under `https://paperspec.scient.ing/vocab#`: `format`, `kind`, `status`, `receipt`, `layer`, `audience`, `visibility`, `purpose`, `project`, `docs`, `pin`, `role`, `symbol`, `key`, `verdict`, `reason`, `reference`, `for`, `category`, `generator`, `exercise`, `anchor`, `rights_use` and the class `AnchorSelector`.
|
||||
Four are renamed: `key`, the key of a `symbol` body's symbol, is `sct:symbolKey`, `for` is `sct:questionFor`, `generator` is `sct:generatedBy`, and `rights_use` is `sct:rightsUse`.
|
||||
The twenty-fifth, `total`, maps to `totalItems` in the ActivityStreams namespace.
|
||||
The file defines 27 terms.
|
||||
Twenty-six map to IRIs under `https://paperspec.scient.ing/vocab#`: `format`, `kind`, `status`, `receipt`, `layer`, `audience`, `visibility`, `purpose`, `project`, `docs`, `pin`, `role`, `symbol`, `key`, `verdict`, `reason`, `reference`, `note`, `proposer`, `for`, `category`, `generator`, `exercise`, `anchor`, `rights_use` and the class `AnchorSelector`.
|
||||
Five are renamed: `key`, the key of a `symbol` body's symbol, is `sct:symbolKey`, `for` is `sct:questionFor`, `generator` is `sct:generatedBy`, `proposer`, the tool that found a trace, is `sct:proposedBy`, and `rights_use` is `sct:rightsUse`.
|
||||
The twenty-seventh, `total`, maps to `totalItems` in the ActivityStreams namespace.
|
||||
Two terms carry a type rule.
|
||||
A processor reads `layer` as an IRI, and it always reads `docs` as a set, so a one-element list stays a list.
|
||||
|
||||
The file declares `anchor` and `exercise` as JSON literals (`"@type": "@json"`, a JSON-LD 1.1 feature, which is why the file sets `"@version": 1.1`).
|
||||
The file declares `anchor`, `exercise` and `proposer` as JSON literals (`"@type": "@json"`, a JSON-LD 1.1 feature, which is why the file sets `"@version": 1.1`).
|
||||
A JSON-LD processor then keeps a `scienting-anchor/1` record intact instead of expanding it.
|
||||
An anchor holds hashes (fixed-length fingerprints of text) and a `span`, a pair of character offsets whose order carries meaning.
|
||||
Expansion would drop the anchor's unmapped field names ([JSON-LD 1.1 API, expansion step 13.3](https://www.w3.org/TR/json-ld11-api/#alg-expand-each-key-value)) and would give its arrays no order ([JSON-LD 1.1 §4.3](https://www.w3.org/TR/json-ld11/#sets-and-lists)).
|
||||
@@ -90,6 +90,13 @@ That is the repository's statement; no Web Annotation client has been tested aga
|
||||
The fallback is not always there.
|
||||
Three of the eleven fixture annotations have only an `AnchorSelector` (the section-level question, the `generated` annotation on a figure and the exercise on a table), because the anchor points at a container and has no quote.
|
||||
|
||||
## A body that points at another paper
|
||||
|
||||
A `trace` body ([spec/18 §18.5](../spec/18-layers.md#185-traced-citations)) follows a citation in the annotated sentence to a passage of the cited paper, so its `source` and `selector` name the cited paper, not the annotated one.
|
||||
[`fixtures/records/traces/annotations.jsonl`](../fixtures/records/traces/annotations.jsonl) holds four annotations on the draft `writer2027fixed-ynr7.v3`, which cites the example paper.
|
||||
The Web Annotation context defines `source` and `selector` for a body as it does for a target, so a trace's selector list puts the `AnchorSelector` first and the `TextQuoteSelector` after it, as a target's does.
|
||||
This context adds the rest of the body: `reference`, the reference of the draft that the sentence cites, `note`, `verdict` and `proposer`, which a processor keeps whole, as it keeps an anchor, because its model's hashes and its score are values and not terms.
|
||||
|
||||
## Using the context
|
||||
|
||||
- Writer: Put both URLs (web addresses), in the order shown, in `@context` of `layer.json` and of every line of `annotations.jsonl`. [Chapter 18](../spec/18-layers.md) defines layers (§18.2), annotations (§18.3) and bodies (§18.4).
|
||||
@@ -97,7 +104,7 @@ Three of the eleven fixture annotations have only an `AnchorSelector` (the secti
|
||||
The file `vectors/resolve.jsonl` resolves seventeen anchors against `fixtures/packages/hale2026harmonic-damb` and gives every outcome.
|
||||
- JSON-LD processor: Use a JSON-LD 1.1 processor; the context relies on `@json` and `@version` 1.1.
|
||||
- Checker: `schemas/annotation.schema.json` and `schemas/layer.schema.json` validate the files.
|
||||
`paperspec check` also checks that each annotation's ID derives from its content and that the receipt at `fixtures/records/layer/receipt.json` covers each `confirmed` annotation.
|
||||
`paperspec check` also checks that each annotation's ID derives from its content and that the receipt of its layer, at `fixtures/records/layer/receipt.json` or `fixtures/records/traces/receipt.json`, covers each `confirmed` annotation.
|
||||
|
||||
The traps:
|
||||
|
||||
|
||||
@@ -25,6 +25,11 @@
|
||||
"verdict": "sct:verdict",
|
||||
"reason": "sct:reason",
|
||||
"reference": "sct:reference",
|
||||
"note": "sct:note",
|
||||
"proposer": {
|
||||
"@id": "sct:proposedBy",
|
||||
"@type": "@json"
|
||||
},
|
||||
"for": "sct:questionFor",
|
||||
"category": "sct:category",
|
||||
"generator": "sct:generatedBy",
|
||||
|
||||
+1
-1
@@ -428,7 +428,7 @@ Keep using it as a reading and distribution format.
|
||||
*Status.*
|
||||
The specification is version 0.1.0.
|
||||
`registry/formats.json` lists 32 formats: 19 are `candidate` (complete, with vectors and fixtures, ready to implement) and 13 are `provisional` (complete; expected to change as implementations gain experience): project, bundle, dataset, digitization, view, snapshot, draft-ids, exercise, submission, grade, checklist, log-entry and checkpoint ([spec/00 §0.4](../spec/00-introduction.md#04-status)).
|
||||
The repository holds 24 chapters, 43 JSON Schemas, 25 vector files, five valid fixture packages, one of them also zipped, and 31 invalid ones, each naming the rule it breaks, and a reference checker, `paperspec check`.
|
||||
The repository holds 24 chapters, 43 JSON Schemas, 26 vector files, five valid fixture packages, one of them also zipped, and 31 invalid ones, each naming the rule it breaks, and a reference checker, `paperspec check`.
|
||||
Licenses: prose CC BY 4.0; schemas, SQL, the JSON-LD context, registries, vectors and fixtures CC0 1.0; the code in `src/` Apache-2.0 ([README.md](../README.md#licenses)).
|
||||
|
||||
*No adoption claim.*
|
||||
|
||||
+3
-1
@@ -376,8 +376,10 @@ Claims are not defined in this repository.
|
||||
A comment, caveat or question also says what it is about with a `category` from a fixed list: a general `comment`, `context`, a `critique`, a `question`, a `correction` of an error in the paper, `feedback` for the authors, or a `resource`.
|
||||
Model-written text is always a `generated` annotation, never source, and a model's annotation stays `proposed` until a person confirms it.
|
||||
Whether a generic Web Annotation client displays an `AnchorSelector` has not been tested; the fallback exists so that it can still use the quote.
|
||||
A `trace` annotation follows a citation to the passage it rests on: it targets the citing sentence and its body names the reference, the cited paper and the passage in it.
|
||||
Whether a trace still leads there is computed from both papers rather than stored: `current`, `changed`, `stale`, `unlinked` or `misresolved`.
|
||||
Layers are `candidate`.
|
||||
Pinned by the layer records in `fixtures/records/layer/`.
|
||||
Pinned by the layer records in `fixtures/records/layer/` and `fixtures/records/traces/`, and by [vectors/traces.jsonl](../vectors/traces.jsonl).
|
||||
See [chapter 18](../spec/18-layers.md).
|
||||
|
||||
## Exercises
|
||||
|
||||
@@ -212,6 +212,38 @@ Receipts by `ana` raise `ec5jc93` and `a4jrz2t` to F3; `xjns57z` stays at F0 bec
|
||||
To compute the final level, check each receipt's signature, revocations and spot-checks, as [chapter 8](../spec/08-fidelity.md#82-levels) describes.
|
||||
The `scope` column of the third query shows the value `targeted:paperclaim`, which is data in the fixture, copied here exactly as the query prints it.
|
||||
|
||||
## Follow a traced citation
|
||||
|
||||
This shows how a citation leads to the passage it rests on, and how to tell that it still does.
|
||||
A trace is an annotation from the sentence that cites a paper to a passage of that paper, with the reference it follows ([chapter 18](../spec/18-layers.md#185-traced-citations)).
|
||||
The draft in the fixtures cites the main example once, and its layer of traces holds three: a model's, a person's, and a candidate a person rejected.
|
||||
The Python package `paperspec.traces` judges each against the current state of both papers:
|
||||
|
||||
```python
|
||||
import json
|
||||
from paperspec import package, traces
|
||||
|
||||
draft = package.read_state("fixtures/packages/writer2027fixed-ynr7.v3")
|
||||
paper = package.read_state("fixtures/packages/hale2026harmonic-damb")
|
||||
same_as = json.load(open("fixtures/packages/hale2026harmonic-damb/document.json"))["work"]["same_as"]
|
||||
annotations = [json.loads(line) for line in open("fixtures/records/traces/annotations.jsonl", encoding="utf-8")]
|
||||
for a in annotations:
|
||||
if a["body"]["kind"] == "trace":
|
||||
r = traces.resolve(a, draft, paper, same_as)
|
||||
print(a["status"], r["result"], r["open"], traces.disputed(a["id"], annotations))
|
||||
```
|
||||
|
||||
```text
|
||||
confirmed current smithlab/hale2026harmonic-damb#mhp0at8@ee42c4[48:60] True
|
||||
rejected current smithlab/hale2026harmonic-damb#kea5026@b836d5[81:105] False
|
||||
confirmed current smithlab/hale2026harmonic-damb#hj4s7pb@6c6fba False
|
||||
```
|
||||
|
||||
Each line gives the trace's status, its outcome, the current address of the passage it leads to, and whether a reply disputes it.
|
||||
The first trace is disputed by a reply that says the table is the better passage, which is where the third trace leads.
|
||||
The outcomes here are all `current`; [vectors/traces.jsonl](../vectors/traces.jsonl) corrects the draft or the paper and shows the others: `changed` when the passage was reworded but is still found, `stale` when it is gone, `unlinked` when the sentence no longer cites the reference, and `misresolved` when the reference now resolves to another paper.
|
||||
Resolving a trace also lists the citations inside the passage, so a chain of "as previously reported" is followed one trace at a time.
|
||||
|
||||
## Test your implementation against a vector
|
||||
|
||||
A test vector is a recorded input together with the output every correct implementation must produce.
|
||||
|
||||
+1
-1
@@ -107,7 +107,7 @@ A `candidate` format is complete, with vectors and fixtures, and ready to implem
|
||||
A `provisional` one is complete and expected to change as implementations gain experience.
|
||||
The provisional formats are dataset, digitization, view, project, bundle, snapshot, draft-ids, exercise, submission, grade, checklist, log-entry and checkpoint.
|
||||
|
||||
The repository holds a reference checker (`paperspec check`), 25 vector files, five valid packages, one of them also as a zip, and 31 deliberately broken packages ([spec/00 §0.4](../spec/00-introduction.md#04-status), [spec/22](../spec/22-conformance.md)).
|
||||
The repository holds a reference checker (`paperspec check`), 26 vector files, five valid packages, one of them also as a zip, and 31 deliberately broken packages ([spec/00 §0.4](../spec/00-introduction.md#04-status), [spec/22](../spec/22-conformance.md)).
|
||||
|
||||
## What does it not do?
|
||||
|
||||
|
||||
@@ -194,6 +194,10 @@ Text policy
|
||||
Tile
|
||||
: One file of a transparency log served statically: 256 hashes of one level of the tree, or 256 entries, so a client fetches only the subtrees it needs. ([23.4](../spec/23-log.md#234-serving-a-log))
|
||||
|
||||
Trace
|
||||
: An annotation that follows one citation of a sentence to the passage of the cited paper it rests on, naming the reference it follows and, when a tool found the passage, the tool, its run, score and rank.
|
||||
Whether it still leads there is computed from both papers as current, changed, stale, unlinked or misresolved, and it is disputed while a reply assesses it. ([18.5](../spec/18-layers.md#185-traced-citations))
|
||||
|
||||
Verdict
|
||||
: A judgment of whether a cited passage supports a citing sentence: supports, partly, does not, or cannot tell. ([19.2](../spec/19-exercises.md#192-verdicts))
|
||||
|
||||
|
||||
+1
-1
@@ -56,7 +56,7 @@ It appears inside hashed data and test vectors, and it is separate from the repo
|
||||
| [registry/](../registry/README.md) | The list of formats, the rule IDs a checker reports, download-stamp rules, and default rights requirements. |
|
||||
| [tools/](../tools/README.md) | How to run the reference commands: `paperspec check` checks everything, `paperspec build` rebuilds the fixtures and vectors, and `paperspec load` loads packages into SQLite. |
|
||||
| [src/paperspec/](../src/paperspec/) | The oracle, a compact implementation of every rule, which the other repositories of the project install as the Python package `paperspec`. |
|
||||
| [vectors/](../vectors/README.md) | 25 files of test vectors: inputs and the exact outputs every implementation must compute. |
|
||||
| [vectors/](../vectors/README.md) | 26 files of test vectors: inputs and the exact outputs every implementation must compute. |
|
||||
| [fixtures/](../fixtures/README.md) | Five valid example packages, one of them also as a zip, 31 deliberately broken ones that each break one named rule, example records, and the test keys that signed them. All text is synthetic. |
|
||||
| [sql/](../sql/README.md) | Tables for SQLite, Dolt and PostgreSQL, and a SQLite bundle with full-text search. |
|
||||
| [context/](../context/README.md) | The JSON-LD context that makes annotation layers W3C Web Annotations. |
|
||||
|
||||
@@ -48,6 +48,7 @@ Each record is a single file, or a folder where a format needs several files.
|
||||
| `profile-acme.json`, `profile-smithlab.json` | What those conditions ask about a team: its purpose, its funding and whom it competes with. |
|
||||
| `roster.json` | The keys that may sign, the role of each key's holder in the team, and what each key may sign. Ana is the owner, so she may revoke anyone's receipt. |
|
||||
| `layer/` | A layer of annotations on the main example (`layer.json`, `annotations.jsonl`) and the receipt that confirms them (`receipt.json`). Its claim link and its symbol name the claim `amber/k-bond` of paperclaim's example store as paperclaim references it, `claim:272XTX7C`, pinned at `claim:272XTX7C@02f47b`; the symbol names `K_r` by its key, `K_{r}`. |
|
||||
| `traces/` | A layer of traced citations on the Typst draft, which cites the main example (`layer.json`, `annotations.jsonl`, `receipt.json`): a trace a model proposed from the draft's citation of "a two-step fit" to the abstract of the main example and a person confirmed, with its receipt; a second candidate, the introduction's sentence, that a person rejected; a person's trace of the same sentence to Table 1; and a person's reply that disputes the first ([spec/18 §18.5](../spec/18-layers.md#185-traced-citations)). `vectors/traces.jsonl` resolves them after corrections. |
|
||||
| `submissions/`, `grades/` | A student's answers to exercises in the layer, and the grader's result for each. |
|
||||
| `suspect.json` | A doubtful reading raised for review: the converter's reading of an exponent in the main example, before the first correction answered it. |
|
||||
| `qa-report.json` | Reviewer error bounds for a delivery that covers two of the main example's receipts, signed by the team's owner key. |
|
||||
|
||||
@@ -1 +1 @@
|
||||
{"claims":{"paperclaim":"amber"},"datasets":["ds-hale-bond-params","ds-hale-ctct-scan"],"documents":[{"doc":"smithlab/hale2026harmonic-damb","reason":"The bond constants we start from.","status":{"Ben Example":"read","Cara Example":"reading"},"tags":["parameters"]},{"doc":"okafor2025solvent-acth","reason":"Solvent shifts for the validation set.","tags":["spectra"]}],"drafts":["smithlab/writer2027fixed-ynr7.v3"],"format":"scienting-project/1","key":"smithlab-charges","layers":["tag:scient.ing,2026:layer:smithlab/kp4m2x7q"],"members":[{"keys":["ana"],"person":"Ana Example","role":"owner"},{"keys":["ben","ben-spot"],"person":"Ben Example","role":"editor"},{"keys":["cara"],"person":"Cara Example","role":"reader"}],"name":"Charges and bonds","paths":[{"id":"onboarding","steps":[{"note":"Read the introduction.","ref":"smithlab/hale2026harmonic-damb","type":"document"},{"note":"Then the table.","ref":"smithlab/hale2026harmonic-damb#hj4s7pb","type":"node"},{"note":"The bond force constant, amber/k-bond.","ref":"claim:272XTX7C","type":"claim"}],"title":"First week"}],"settings":{"digit_rule":"anchored-sections","drift_threshold":"0.25","log":{"origin":"scient.ing/log/smithlab","threshold":1,"witnesses":["wit"]},"rights_unknown":"refuse","spot_check":{"minimum":10,"rate":"0.05"},"stages":"scienting-stages-default"},"store":"smithlab","views":["ctct-scan"]}
|
||||
{"claims":{"paperclaim":"amber"},"datasets":["ds-hale-bond-params","ds-hale-ctct-scan"],"documents":[{"doc":"smithlab/hale2026harmonic-damb","reason":"The bond constants we start from.","status":{"Ben Example":"read","Cara Example":"reading"},"tags":["parameters"]},{"doc":"okafor2025solvent-acth","reason":"Solvent shifts for the validation set.","tags":["spectra"]}],"drafts":["smithlab/writer2027fixed-ynr7.v3"],"format":"scienting-project/1","key":"smithlab-charges","layers":["tag:scient.ing,2026:layer:smithlab/kp4m2x7q","tag:scient.ing,2026:layer:smithlab/tr6c3q8m"],"members":[{"keys":["ana"],"person":"Ana Example","role":"owner"},{"keys":["ben","ben-spot"],"person":"Ben Example","role":"editor"},{"keys":["cara"],"person":"Cara Example","role":"reader"}],"name":"Charges and bonds","paths":[{"id":"onboarding","steps":[{"note":"Read the introduction.","ref":"smithlab/hale2026harmonic-damb","type":"document"},{"note":"Then the table.","ref":"smithlab/hale2026harmonic-damb#hj4s7pb","type":"node"},{"note":"The bond force constant, amber/k-bond.","ref":"claim:272XTX7C","type":"claim"}],"title":"First week"}],"settings":{"digit_rule":"anchored-sections","drift_threshold":"0.25","log":{"origin":"scient.ing/log/smithlab","threshold":1,"witnesses":["wit"]},"rights_unknown":"refuse","spot_check":{"minimum":10,"rate":"0.05"},"stages":"scienting-stages-default"},"store":"smithlab","views":["ctct-scan"]}
|
||||
@@ -0,0 +1,4 @@
|
||||
{"@context":["http://www.w3.org/ns/anno.jsonld","https://paperspec.scient.ing/context/scienting-1.jsonld"],"body":{"kind":"trace","note":"The abstract states the two steps: a scan first, then vibrational frequencies.","proposer":{"model":{"name":"example-model","parameters":"998b5161e39e01c8bc43ef81ec89141e15b7ea53b2e936707920a6097dbcb2c5","processor":"local","prompt":"9cc338fba7c62db7266f530cb555a5ee36910d41a0120a3fe7fd7de1944d0931","provider":"Example Models (illustrative)","version":"2026-09","weights":"504b2d340045b92c22682b60020c21103aae7ff41172bb632fbe305eb4af9b5e"},"rank":1,"run":"illustrative-run-2","score":"0.8731"},"reference":"ebcq5cy","selector":[{"anchor":{"doc":"hale2026harmonic-damb","format":"scienting-anchor/1","kind":"sentence","node":"mhp0at8","node_hash":"ee42c4780724a411697e3a95daf182d37c792d4689a7172f32929785d5fd968e","own_hash":"24cc437784bf358d639755dbf59984ad2c0c7a0e7f9448b23e965039b67f759b","quote":{"exact":"in two steps","prefix":"bond constants for twelve bonds ","suffix":", first to a scan and then to vi"},"rev":12,"span":[48,60],"store":"smithlab"},"type":"AnchorSelector"},{"exact":"in two steps","prefix":"bond constants for twelve bonds ","suffix":", first to a scan and then to vi","type":"TextQuoteSelector"}],"source":"tag:scient.ing,2026:doc:smithlab/hale2026harmonic-damb","type":"SpecificResource","verdict":"supports"},"created":"2026-10-12T09:00:00Z","creator":{"id":"tag:scient.ing,2026:software:example-model","name":"A model (illustrative)","type":"Software"},"id":"tag:scient.ing,2026:annotation:smithlab/jh0e4st2c0kpt","layer":"tag:scient.ing,2026:layer:smithlab/tr6c3q8m","motivation":"linking","receipt":"qvckzaatz5d1v","status":"confirmed","target":{"selector":[{"anchor":{"doc":"writer2027fixed-ynr7.v3","format":"scienting-anchor/1","kind":"sentence","node":"qgh1k31","node_hash":"93a39490870e694299e88e6f475d42c38078d1442959e555bdbdf58488dcf187","own_hash":"e004436e7b28ff3dc41e0056f231ba707d9be985694e68acb911345a547842ed","quote":{"exact":"two-step fit","prefix":"Its constants come from a ","suffix":" (Hale and Osei, Table 1).1"},"rev":0,"span":[26,38],"store":"smithlab"},"type":"AnchorSelector"},{"exact":"two-step fit","prefix":"Its constants come from a ","suffix":" (Hale and Osei, Table 1).1","type":"TextQuoteSelector"}],"source":"tag:scient.ing,2026:doc:smithlab/writer2027fixed-ynr7.v3","type":"SpecificResource"},"type":"Annotation"}
|
||||
{"@context":["http://www.w3.org/ns/anno.jsonld","https://paperspec.scient.ing/context/scienting-1.jsonld"],"body":{"kind":"trace","note":"Says harmonic terms are common, not how the constants were fitted.","proposer":{"model":{"name":"example-model","parameters":"998b5161e39e01c8bc43ef81ec89141e15b7ea53b2e936707920a6097dbcb2c5","processor":"local","prompt":"9cc338fba7c62db7266f530cb555a5ee36910d41a0120a3fe7fd7de1944d0931","provider":"Example Models (illustrative)","version":"2026-09","weights":"504b2d340045b92c22682b60020c21103aae7ff41172bb632fbe305eb4af9b5e"},"rank":2,"run":"illustrative-run-2","score":"0.6410"},"reference":"ebcq5cy","selector":[{"anchor":{"doc":"hale2026harmonic-damb","format":"scienting-anchor/1","kind":"sentence","node":"kea5026","node_hash":"b836d5872ca37471fa996be04c538254a8b1e37ca12e8f1b4a40837b503afa33","own_hash":"d673d236c03d9583ecf6e01fa819765ec324dc684bd9bbc6967fa50f216e34d2","quote":{"exact":"classical force fields.1","prefix":"ption of covalent stretching in "},"rev":12,"span":[81,105],"store":"smithlab"},"type":"AnchorSelector"},{"exact":"classical force fields.1","prefix":"ption of covalent stretching in ","suffix":"","type":"TextQuoteSelector"}],"source":"tag:scient.ing,2026:doc:smithlab/hale2026harmonic-damb","type":"SpecificResource"},"created":"2026-10-12T09:00:00Z","creator":{"id":"tag:scient.ing,2026:software:example-model","name":"A model (illustrative)","type":"Software"},"id":"tag:scient.ing,2026:annotation:smithlab/arm3zmj6be9fz","layer":"tag:scient.ing,2026:layer:smithlab/tr6c3q8m","motivation":"linking","status":"rejected","target":{"selector":[{"anchor":{"doc":"writer2027fixed-ynr7.v3","format":"scienting-anchor/1","kind":"sentence","node":"qgh1k31","node_hash":"93a39490870e694299e88e6f475d42c38078d1442959e555bdbdf58488dcf187","own_hash":"e004436e7b28ff3dc41e0056f231ba707d9be985694e68acb911345a547842ed","quote":{"exact":"two-step fit","prefix":"Its constants come from a ","suffix":" (Hale and Osei, Table 1).1"},"rev":0,"span":[26,38],"store":"smithlab"},"type":"AnchorSelector"},{"exact":"two-step fit","prefix":"Its constants come from a ","suffix":" (Hale and Osei, Table 1).1","type":"TextQuoteSelector"}],"source":"tag:scient.ing,2026:doc:smithlab/writer2027fixed-ynr7.v3","type":"SpecificResource"},"type":"Annotation"}
|
||||
{"@context":["http://www.w3.org/ns/anno.jsonld","https://paperspec.scient.ing/context/scienting-1.jsonld"],"body":{"kind":"trace","note":"Table 1 lists the fitted constants of both parameter sets.","reference":"ebcq5cy","selector":[{"anchor":{"doc":"hale2026harmonic-damb","format":"scienting-anchor/1","kind":"table","node":"hj4s7pb","node_hash":"6c6fba200faf41cd06fa8b64e06e2aa29ff8848568b859ae42d1902596348643","own_hash":"66310a9ab1c92568f4c6cae012e35d4245d4ca4b89d2542b7c2113a31ad379ed","rev":12,"store":"smithlab"},"type":"AnchorSelector"}],"source":"tag:scient.ing,2026:doc:smithlab/hale2026harmonic-damb","type":"SpecificResource","verdict":"supports"},"created":"2026-10-12T09:10:00Z","creator":{"id":"tag:scient.ing,2026:key:ana","name":"Ana Example","type":"Person"},"id":"tag:scient.ing,2026:annotation:smithlab/fhnynfb8jx0wc","layer":"tag:scient.ing,2026:layer:smithlab/tr6c3q8m","motivation":"linking","receipt":"qvckzaatz5d1v","status":"confirmed","target":{"selector":[{"anchor":{"doc":"writer2027fixed-ynr7.v3","format":"scienting-anchor/1","kind":"sentence","node":"qgh1k31","node_hash":"93a39490870e694299e88e6f475d42c38078d1442959e555bdbdf58488dcf187","own_hash":"e004436e7b28ff3dc41e0056f231ba707d9be985694e68acb911345a547842ed","quote":{"exact":"Its constants come from a two-step fit (Hale and Osei, Table 1).1"},"rev":0,"store":"smithlab"},"type":"AnchorSelector"},{"exact":"Its constants come from a two-step fit (Hale and Osei, Table 1).1","prefix":"","suffix":"","type":"TextQuoteSelector"}],"source":"tag:scient.ing,2026:doc:smithlab/writer2027fixed-ynr7.v3","type":"SpecificResource"},"type":"Annotation"}
|
||||
{"@context":["http://www.w3.org/ns/anno.jsonld","https://paperspec.scient.ing/context/scienting-1.jsonld"],"body":{"category":"critique","kind":"comment","type":"TextualBody","value":"The abstract names the two steps but not the constants; the table is the better passage."},"created":"2026-10-12T10:00:00Z","creator":{"id":"tag:scient.ing,2026:key:ben","name":"Ben Example","type":"Person"},"id":"tag:scient.ing,2026:annotation:smithlab/g4fm8f93czwkk","layer":"tag:scient.ing,2026:layer:smithlab/tr6c3q8m","motivation":"assessing","status":"proposed","target":{"source":"tag:scient.ing,2026:annotation:smithlab/jh0e4st2c0kpt","type":"SpecificResource"},"type":"Annotation"}
|
||||
@@ -0,0 +1 @@
|
||||
{"@context":["http://www.w3.org/ns/anno.jsonld","https://paperspec.scient.ing/context/scienting-1.jsonld"],"audience":"expert","created":"2026-10-12T09:00:00Z","creator":{"id":"tag:scient.ing,2026:key:ana","name":"Ana Example","type":"Person"},"docs":["smithlab/writer2027fixed-ynr7.v3"],"format":"scienting-layer/1","id":"tag:scient.ing,2026:layer:smithlab/tr6c3q8m","label":"Where the draft's citations lead","license":{"spdx":"CC-BY-4.0"},"project":"smithlab-charges","purpose":"Traces from the draft's citations of Hale and Osei to the passages they rest on.","total":4,"type":"AnnotationCollection","visibility":"team"}
|
||||
@@ -0,0 +1 @@
|
||||
{"at":"2026-10-12T09:30:00Z","by":"ana","doc":"writer2027fixed-ynr7.v3","evidence":[],"format":"scienting-receipt/1","id":"qvckzaatz5d1v","level":"F3","nodes":[],"records":[["annotation","fhnynfb8jx0wc","a1115243473b190f7ed8877780f29c935f29801d6c9fe0b423601247ebf078f8"],["annotation","jh0e4st2c0kpt","b774528344f096051420b6df73c089fdce4d0e1093e4ea3d845fff023c17dee7"]],"rev":0,"root":"d1b96ae5b9887ae843cd209d7da5ea9bb454f1bab77b93d05d38fbfc2b21e826","scope":"targeted:traces","signature":{"key":"ana","scheme":"ed25519","value":"d2xj2bQY3AoDEVV7yzs31mQpMoGre+V919NUM/Ba/qj+ySb1QKs7D9ZidM6+ksl1xlNsCLNU2rdeclUduoZZBA=="}}
|
||||
@@ -448,6 +448,16 @@
|
||||
"description": "The exercise kind is unknown.",
|
||||
"chapter": "spec/22-conformance.md"
|
||||
},
|
||||
{
|
||||
"id": "layer.trace.source",
|
||||
"description": "A trace's passage is not in the document its source names, its source is the citing document, its selector has no AnchorSelector, or its TextQuoteSelector does not repeat the anchor's plain quote.",
|
||||
"chapter": "spec/18-layers.md"
|
||||
},
|
||||
{
|
||||
"id": "layer.trace.target",
|
||||
"description": "A trace's target is not a leaf with runs of the citing document selected by an AnchorSelector, or its sentence does not cite the reference the trace names.",
|
||||
"chapter": "spec/18-layers.md"
|
||||
},
|
||||
{
|
||||
"id": "package.file.canonical",
|
||||
"description": "A JSON or JSON Lines record is not in canonical JSON.",
|
||||
|
||||
@@ -15,6 +15,50 @@
|
||||
"feedback",
|
||||
"resource"
|
||||
]
|
||||
},
|
||||
"selector": {
|
||||
"description": "A selector of a passage: the AnchorSelector, or the standard TextQuoteSelector that repeats the anchor's plain quote for tools that do not know ours (spec/18 §18.3).",
|
||||
"oneOf": [
|
||||
{
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"type": {
|
||||
"const": "AnchorSelector"
|
||||
},
|
||||
"anchor": {
|
||||
"$ref": "anchor.schema.json"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"type",
|
||||
"anchor"
|
||||
],
|
||||
"additionalProperties": false
|
||||
},
|
||||
{
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"type": {
|
||||
"const": "TextQuoteSelector"
|
||||
},
|
||||
"exact": {
|
||||
"type": "string",
|
||||
"minLength": 1
|
||||
},
|
||||
"prefix": {
|
||||
"type": "string"
|
||||
},
|
||||
"suffix": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"type",
|
||||
"exact"
|
||||
],
|
||||
"additionalProperties": false
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"type": "object",
|
||||
@@ -423,6 +467,79 @@
|
||||
],
|
||||
"additionalProperties": false
|
||||
},
|
||||
{
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"type": {
|
||||
"const": "SpecificResource"
|
||||
},
|
||||
"kind": {
|
||||
"const": "trace"
|
||||
},
|
||||
"source": {
|
||||
"description": "The cited document, as a target's source names a document.",
|
||||
"type": "string",
|
||||
"pattern": "^tag:scient\\.ing,2026:doc:[a-z0-9][a-z0-9._-]*/[a-z][a-z0-9]*-[0-9a-hjkmnp-tv-z]{4,51}(\\.[a-z0-9][a-z0-9-]*)*$"
|
||||
},
|
||||
"selector": {
|
||||
"description": "The passage of the cited document: an AnchorSelector, then a TextQuoteSelector when the anchor's quote is plain.",
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/$defs/selector"
|
||||
},
|
||||
"minItems": 1
|
||||
},
|
||||
"reference": {
|
||||
"description": "The reference node of the citing document that the target's sentence cites and the trace is about.",
|
||||
"$ref": "common.schema.json#/$defs/nodeId"
|
||||
},
|
||||
"note": {
|
||||
"type": "string",
|
||||
"minLength": 1
|
||||
},
|
||||
"verdict": {
|
||||
"enum": [
|
||||
"supports",
|
||||
"partly",
|
||||
"does-not",
|
||||
"cannot-tell"
|
||||
]
|
||||
},
|
||||
"proposer": {
|
||||
"description": "The tool that proposed the trace, required when the annotation's creator is Software (spec/18 §18.4).",
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"model": {
|
||||
"$ref": "common.schema.json#/$defs/model"
|
||||
},
|
||||
"run": {
|
||||
"type": "string",
|
||||
"minLength": 1
|
||||
},
|
||||
"score": {
|
||||
"$ref": "common.schema.json#/$defs/decimal"
|
||||
},
|
||||
"rank": {
|
||||
"type": "integer",
|
||||
"minimum": 1
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"model",
|
||||
"run"
|
||||
],
|
||||
"additionalProperties": false
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"type",
|
||||
"kind",
|
||||
"source",
|
||||
"selector",
|
||||
"reference"
|
||||
],
|
||||
"additionalProperties": false
|
||||
},
|
||||
{
|
||||
"type": "object",
|
||||
"properties": {
|
||||
@@ -764,6 +881,46 @@
|
||||
"receipt"
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"description": "A tool's trace names the tool and the run that proposed it (spec/18 §18.4).",
|
||||
"if": {
|
||||
"properties": {
|
||||
"creator": {
|
||||
"properties": {
|
||||
"type": {
|
||||
"const": "Software"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"type"
|
||||
]
|
||||
},
|
||||
"body": {
|
||||
"properties": {
|
||||
"kind": {
|
||||
"const": "trace"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"kind"
|
||||
]
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"creator",
|
||||
"body"
|
||||
]
|
||||
},
|
||||
"then": {
|
||||
"properties": {
|
||||
"body": {
|
||||
"required": [
|
||||
"proposer"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -52,6 +52,7 @@ Replies are annotations that target annotations, so discussions are threads anch
|
||||
| `citation-context` | `TextualBody` | `reference`, `verdict`, `reason` | Why a sentence cites a reference, and whether it supports it. |
|
||||
| `prerequisite` | `SpecificResource` | `source` | What to read or know first. |
|
||||
| `crosslink` | `SpecificResource` | `source`, `selector` | That the span corresponds to a span in another document. |
|
||||
| `trace` | `SpecificResource` | `source`, `selector`, `reference`, `note`, `verdict`, `proposer` | That the target's sentence cites `reference` on the strength of a passage of the cited document ([18.5](#185-traced-citations)). |
|
||||
| `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. `generator` is `{model, run}` as a correction's model author is ([11.2](11-corrections.md#112-every-correction-records)). |
|
||||
@@ -87,3 +88,47 @@ 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, each comment, caveat and question with its category, a claim link and a symbol that reference a paperclaim claim and pin it, and the receipt that confirms some of them.
|
||||
|
||||
## 18.5 Traced citations
|
||||
|
||||
"As previously reported [12]": a reader who finds where the cited paper reports it marks the passage, and the `trace` body keeps the mark.
|
||||
A `crosslink` says only that two spans correspond, in both directions, whereas a trace goes from one sentence's citation to one passage, and says which reference it follows, why, and who or what found it.
|
||||
|
||||
The annotation's target is a leaf or span anchor ([6.4](06-anchors.md#64-four-kinds-of-anchor)) on the citing sentence, which MUST be a leaf with runs, a sentence or a cell, and MUST cite the reference the body names: one of its `cite` runs lists `reference` ([3.5](03-nodes.md#35-inline-runs)).
|
||||
`reference` is the ID of a `reference` node of the citing document, because a sentence may cite several references at once, as in `[3, 12]`, and the target says only which sentence.
|
||||
`source` is the cited document, written as a target's source is, `tag:scient.ing,2026:doc:<store>/<key>`, and never the citing document itself.
|
||||
`selector` selects the passage in it: an `AnchorSelector` whose anchor holds the passage's pin (its `node_hash`) and, for part of a leaf, its `span`, then a `TextQuoteSelector` when the anchor's quote is plain ([18.3](#183-the-annotation)).
|
||||
The passage may be a container, such as a table, whose anchor has no quote.
|
||||
|
||||
| Property | Required | Holds |
|
||||
| --- | --- | --- |
|
||||
| `reference` | Yes | The reference node of the citing document that the sentence cites and the trace follows. |
|
||||
| `source`, `selector` | Yes | The cited document and the passage in it. |
|
||||
| `note` | No | What the passage reports, in a person's or a tool's words. |
|
||||
| `verdict` | No | `supports`, `partly`, `does-not` or `cannot-tell`, as a `citation-context` body has it ([19.2](19-exercises.md#192-verdicts)). |
|
||||
| `proposer` | When the creator is `Software` | `{model, run, score?, rank?}`: the model and run that found the passage as a candidate, as a `generated` body's `generator` names them, the candidate's `score` as a decimal string, and its `rank`, from 1, among the candidates the run proposed for this citation. |
|
||||
|
||||
The state of a trace is the annotation's `status`.
|
||||
A trace a tool proposes is `proposed` until a person confirms it, and `rejected` when a person turns the candidate down; a rejected candidate is kept, so that the tool is not offered it again.
|
||||
A trace is *disputed* while a reply to it, an annotation whose target is the trace and whose motivation is `assessing`, is `proposed` or `confirmed`.
|
||||
The dispute is computed from the replies and never written on the trace, so a reply can be withdrawn.
|
||||
|
||||
Whether a trace still leads where it did is also computed, from the current state of both documents.
|
||||
Resolve the target's anchor against the citing document and the body's anchor against the cited document ([6.5](06-anchors.md#65-resolution)).
|
||||
The first row that applies gives the outcome:
|
||||
|
||||
| Outcome | When |
|
||||
| --- | --- |
|
||||
| `stale` | An anchor is stale, on the citing side, the cited side or both. |
|
||||
| `unlinked` | The sentence no longer cites `reference`: a `relink` removed the citation ([11.7](11-corrections.md#117-citations-and-references)), or the reference was deleted. |
|
||||
| `misresolved` | A `resolve` made the reference name a DOI or a document that is not the cited work. A resolution of `none` names nothing and contradicts nothing ([10](10-references.md)). |
|
||||
| `changed` | Both anchors resolve and at least one is not `current`. |
|
||||
| `current` | Both anchors are `current`, and the reference still resolves to the cited work or to nothing. |
|
||||
|
||||
A resolution names the cited work when its document is the cited document, another version of the same work (a key with the same work key, [2.4](02-model.md#24-work-and-document-keys)), or one the caller knows to be the work; and a `doi` resolution does when the work is known by that DOI (`same_as`, [2.4](02-model.md#24-work-and-document-keys)).
|
||||
Besides the outcome, a resolution gives the passage's current address, `key#node@pin[start:end]`, while its anchor resolves, and the citations inside the passage.
|
||||
A chain of "as previously reported" is then followed one trace at a time: the reader opens the passage, and a citation in it that has a trace of its own leads on.
|
||||
|
||||
A trace breaks `layer.trace.source` when its selector has no `AnchorSelector`, an anchor is in another document than `source` names, `source` is the citing document, or the `TextQuoteSelector` does not repeat the anchor's plain quote.
|
||||
It breaks `layer.trace.target` when the target is not a leaf with runs of the citing document selected by an `AnchorSelector`, or the sentence does not cite `reference` ([22.4](22-conformance.md#224-rules)).
|
||||
`vectors/traces.jsonl` resolves traces against the fixture packages after corrections, with every outcome and every refusal, and `fixtures/records/traces/` holds a layer of four annotations on `writer2027fixed-ynr7.v3` that cites `hale2026harmonic-damb`: a trace a model proposed to the paper's abstract and a person confirmed with a receipt, a candidate a person rejected, a person's trace to Table 1, and a reply that disputes the first.
|
||||
@@ -6,7 +6,7 @@
|
||||
| --- | --- | --- |
|
||||
| `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, 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 `paperspec load --bundle`. | Accepts each. |
|
||||
| `fixtures/records/` | A valid record of every other format that is a JSON file of its own, and a second layer, of traced citations ([18.5](18-layers.md#185-traced-citations)), whose traces resolve as `vectors/traces.jsonl` says; the stage requirements and stamp rules are in `registry/`, and a bundle is built by `paperspec load --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. |
|
||||
|
||||
@@ -49,6 +49,10 @@ Rule IDs are stable: a rule's meaning never changes, and a retired rule's ID is
|
||||
| `doc.receipt.`, `doc.qa.`, `doc.fidelity.` | Receipts, spot-checks and levels. |
|
||||
| `doc.witness.`, `doc.page.`, `doc.location.`, `doc.reference.` | Witnesses and geometry. |
|
||||
| `grade.` | Grading. |
|
||||
| `layer.` | Annotations that refer to documents: `layer.trace.source` and `layer.trace.target` ([18.5](18-layers.md#185-traced-citations)). |
|
||||
|
||||
A package is validated by [22.2](#222-validating-a-package), but a layer is not part of one.
|
||||
The `layer.` rules are reported for an annotation, in the form `annotation: rule: message`, by a validator that has the annotation and the states of the documents it names; the reference implementation does so for traces in `paperspec.traces.problems`.
|
||||
|
||||
## 22.5 Stability
|
||||
|
||||
|
||||
+1
-1
@@ -68,7 +68,7 @@ CREATE TABLE layers (
|
||||
CREATE TABLE annotations (
|
||||
id VARCHAR(256) NOT NULL,
|
||||
layer VARCHAR(256) NOT NULL,
|
||||
body_kind VARCHAR(24) NOT NULL CHECK (body_kind IN ('gloss', 'claim-link', 'symbol', 'comment', 'caveat', 'question', 'exercise', 'citation-context', 'prerequisite', 'crosslink', 'translation', 'description', 'generated')),
|
||||
body_kind VARCHAR(24) NOT NULL CHECK (body_kind IN ('gloss', 'claim-link', 'symbol', 'comment', 'caveat', 'question', 'exercise', 'citation-context', 'prerequisite', 'crosslink', 'trace', 'translation', 'description', 'generated')),
|
||||
category VARCHAR(12) CHECK (category IN ('comment', 'context', 'critique', 'question', 'correction', 'feedback', 'resource')),
|
||||
status VARCHAR(10) NOT NULL CHECK (status IN ('proposed', 'confirmed', 'rejected', 'withdrawn')),
|
||||
receipt VARCHAR(32),
|
||||
|
||||
+204
-4
@@ -26,10 +26,10 @@ from paperspec import align as AL # noqa: E402
|
||||
from paperspec import anchors as AN # noqa: E402
|
||||
from paperspec import corrections as CO # noqa: E402
|
||||
from paperspec import crypto, fingerprint as FP, grade as GR, hashing, jcs, receipts as RC, rights as RI # noqa: E402
|
||||
from paperspec import keys as K, log as LG, mldsa, targets as TG, vrf # noqa: E402
|
||||
from paperspec import keys as K, log as LG, mldsa, targets as TG, traces as TR, vrf # noqa: E402
|
||||
from paperspec.model import LEAVES, MATH_KINDS, RUN_KINDS, VERBATIM_KINDS, Doc # noqa: E402
|
||||
from paperspec.notation import fold_math # noqa: E402
|
||||
from paperspec.package import ZIP_EXTENSION, history_items, jsonl, line, witness_id, write, write_zip # noqa: E402
|
||||
from paperspec.package import ZIP_EXTENSION, history_items, jsonl, line, read_state, witness_id, write, write_zip # noqa: E402
|
||||
from paperspec.schema import Store # noqa: E402
|
||||
from paperspec.text import letter_first, mint_node_id, norm, record_id, sha256_hex # noqa: E402
|
||||
from paperspec.tree import Refusal, _flatten, mint_ids, validate_structure # noqa: E402
|
||||
@@ -1394,6 +1394,8 @@ def ann_hash(a):
|
||||
TAG = "tag:scient.ing,2026:"
|
||||
# The claim amber/k-bond of paperclaim's example store, by its tag; paperclaim prints its pin as claim:272XTX7C@02f47b.
|
||||
CLAIM_K_BOND = "claim:272XTX7C"
|
||||
# The layer of traced citations on the draft (spec/18 §18.5), which the project holds beside the example layer.
|
||||
TRACE_LAYER = TAG + "layer:smithlab/tr6c3q8m"
|
||||
# A model's provenance: enough to name the weights and the instructions it ran with (all illustrative).
|
||||
MODEL = {"name": "example-model", "provider": "Example Models (illustrative)", "version": "2026-09",
|
||||
"weights": sha256_hex("illustrative weights"), "prompt": sha256_hex("illustrative review prompt"),
|
||||
@@ -1600,7 +1602,7 @@ def records(out, hale):
|
||||
{"type": "document", "ref": QHALE, "note": "Read the introduction."},
|
||||
{"type": "node", "ref": QHALE + "#" + I["@tab1"], "note": "Then the table."},
|
||||
{"type": "claim", "ref": CLAIM_K_BOND, "note": "The bond force constant, amber/k-bond."}]}],
|
||||
"layers": [layer_id], "datasets": ["ds-hale-bond-params", "ds-hale-ctct-scan"], "views": ["ctct-scan"],
|
||||
"layers": [layer_id, TRACE_LAYER], "datasets": ["ds-hale-bond-params", "ds-hale-ctct-scan"], "views": ["ctct-scan"],
|
||||
"drafts": [STORE + "/" + DRAFT], "claims": {"paperclaim": "amber"},
|
||||
"settings": {"digit_rule": "anchored-sections", "drift_threshold": "0.25",
|
||||
"spot_check": {"rate": "0.05", "minimum": 10}, "rights_unknown": "refuse",
|
||||
@@ -1761,6 +1763,203 @@ def records(out, hale):
|
||||
wr(out, "draft-ids.jsonl", jsonl(lines))
|
||||
|
||||
|
||||
# --- traced citations ---------------------------------------------------------------
|
||||
|
||||
|
||||
def traces(out, hale, draft):
|
||||
"""The layer of traced citations on the draft, which cites the main example (spec/18 §18.5), and
|
||||
vectors/traces.jsonl: each trace resolved against the two packages, after corrections that make it current,
|
||||
changed, stale, unlinked or misresolved, and the traces that break `layer.trace.source` or `layer.trace.target`."""
|
||||
HALE, WRITER = hale["key"], draft["key"]
|
||||
QHALE, QWRITER = STORE + "/" + HALE, STORE + "/" + WRITER
|
||||
OKAFOR = Names.registry["okafor2025open"].key
|
||||
pkgs = os.path.join(out, "fixtures", "packages")
|
||||
cited = read_state(os.path.join(pkgs, HALE))
|
||||
citing = read_state(os.path.join(pkgs, WRITER))
|
||||
hdoc, wdoc = cited.doc(), citing.doc()
|
||||
hvals, wvals = hashing.compute(hdoc), hashing.compute(wdoc)
|
||||
sentences = {hashing.plain(hdoc, n): n for n in hdoc.order if hdoc.nodes[n]["kind"] == "sentence"}
|
||||
sentence = next(n for t, n in sentences.items() if t.startswith("We fit harmonic bond constants"))
|
||||
intro = next(n for t, n in sentences.items() if t.startswith("Harmonic bond terms remain"))
|
||||
table = hale["names"].ids["@tab1"]
|
||||
cell = hale["names"].ids["@ow_ak"]
|
||||
sentence_citing = draft["names"].ids["@d2"]
|
||||
reference = draft["names"].ids["@dr1"]
|
||||
other_sentence = draft["names"].ids["@d1"]
|
||||
|
||||
def anchor(doc, vals, rev, nid, quote=None):
|
||||
span = None
|
||||
if quote:
|
||||
t = hashing.plain(doc, nid)
|
||||
i = t.index(quote)
|
||||
span = [i, i + len(quote)]
|
||||
return AN.make_anchor(doc, vals, nid, rev, span=span, store=STORE)
|
||||
|
||||
def selector(a):
|
||||
return [{"type": "AnchorSelector", "anchor": a}] + (
|
||||
[{"type": "TextQuoteSelector", "exact": a["quote"]["exact"], "prefix": a["quote"].get("prefix", ""),
|
||||
"suffix": a["quote"].get("suffix", "")}] if "quote" in a and "exact" in a["quote"] else [])
|
||||
|
||||
def target(a):
|
||||
return {"type": "SpecificResource", "source": TAG + "doc:" + QWRITER, "selector": selector(a)}
|
||||
|
||||
def body(passage, note=None, verdict=None, proposer=None):
|
||||
b = {"type": "SpecificResource", "kind": "trace", "source": TAG + "doc:" + QHALE, "selector": selector(passage),
|
||||
"reference": reference}
|
||||
for k, v in (("note", note), ("verdict", verdict), ("proposer", proposer)):
|
||||
if v is not None:
|
||||
b[k] = v
|
||||
return b
|
||||
|
||||
ana = {"id": TAG + "key:ana", "type": "Person", "name": "Ana Example"}
|
||||
ben = {"id": TAG + "key:ben", "type": "Person", "name": "Ben Example"}
|
||||
model = {"id": TAG + "software:example-model", "type": "Software", "name": "A model (illustrative)"}
|
||||
|
||||
def ann(b, tgt, status, who, created, motivation="linking"):
|
||||
a = {"@context": CTX, "type": "Annotation", "layer": TRACE_LAYER, "motivation": motivation, "creator": who,
|
||||
"created": created, "body": b, "target": tgt}
|
||||
a["id"] = TAG + "annotation:smithlab/" + record_id(H(jcs.dumpb(a)))
|
||||
a["status"] = status
|
||||
return a
|
||||
|
||||
span_citing = anchor(wdoc, wvals, citing.rev, sentence_citing, "two-step fit")
|
||||
whole_citing = anchor(wdoc, wvals, citing.rev, sentence_citing)
|
||||
hrev = cited.rev
|
||||
run = "illustrative-run-2"
|
||||
# A model found the abstract for the citation of "a two-step fit" and a person confirmed it; its second candidate,
|
||||
# the introduction's sentence with a citation of its own, a person turned down.
|
||||
best = ann(body(anchor(hdoc, hvals, hrev, sentence, "in two steps"),
|
||||
note="The abstract states the two steps: a scan first, then vibrational frequencies.",
|
||||
verdict="supports", proposer={"model": MODEL, "run": run, "score": "0.8731", "rank": 1}),
|
||||
target(span_citing), "confirmed", model, "2026-10-12T09:00:00Z")
|
||||
second = ann(body(anchor(hdoc, hvals, hrev, intro, "classical force fields.1"),
|
||||
note="Says harmonic terms are common, not how the constants were fitted.",
|
||||
proposer={"model": MODEL, "run": run, "score": "0.6410", "rank": 2}),
|
||||
target(span_citing), "rejected", model, "2026-10-12T09:00:00Z")
|
||||
# A person's trace for the same sentence as a whole: the table it points at.
|
||||
person = ann(body(anchor(hdoc, hvals, hrev, table), note="Table 1 lists the fitted constants of both parameter sets.",
|
||||
verdict="supports"),
|
||||
target(whole_citing), "confirmed", ana, "2026-10-12T09:10:00Z")
|
||||
reply = ann({"type": "TextualBody", "kind": "comment", "category": "critique",
|
||||
"value": "The abstract names the two steps but not the constants; the table is the better passage."},
|
||||
{"type": "SpecificResource", "source": best["id"]}, "proposed", ben, "2026-10-12T10:00:00Z", "assessing")
|
||||
anns = [best, second, person, reply]
|
||||
confirmed = [a for a in anns if a["status"] == "confirmed"]
|
||||
lr = RC.seal({"format": "scienting-receipt/1", "doc": WRITER, "rev": citing.rev, "level": "F3",
|
||||
"scope": "targeted:traces", "nodes": [],
|
||||
"records": sorted([["annotation", a["id"].rsplit("/", 1)[1], ann_hash(a)] for a in confirmed]),
|
||||
"evidence": [], "by": "ana", "at": "2026-10-12T09:30:00Z"}, secret("ana"), "ana")
|
||||
for a in confirmed:
|
||||
a["receipt"] = lr["id"]
|
||||
wr(out, "traces/layer.json", {"@context": CTX, "format": "scienting-layer/1", "id": TRACE_LAYER,
|
||||
"type": "AnnotationCollection", "label": "Where the draft's citations lead",
|
||||
"purpose": "Traces from the draft's citations of Hale and Osei to the passages they rest on.",
|
||||
"audience": "expert", "visibility": "team", "license": {"spdx": "CC-BY-4.0"},
|
||||
"creator": ana, "created": "2026-10-12T09:00:00Z", "project": "smithlab-charges",
|
||||
"docs": [QWRITER], "total": len(anns)})
|
||||
wr(out, "traces/annotations.jsonl", jsonl(anns))
|
||||
wr(out, "traces/receipt.json", lr)
|
||||
|
||||
# The vectors. A case names the two packages, the corrections that bring each to the state to judge, and the
|
||||
# annotation; the expected outcome is computed here, never typed.
|
||||
base = {"author": {"key": "ana"}, "reason": "Vector.", "at": "2026-10-13T12:00:00Z"}
|
||||
person_ev = [{"kind": "person", "by": "ana"}]
|
||||
crop_ev = [{"kind": "crop", "sha256": crop_sha(cell)}]
|
||||
|
||||
def corr(c):
|
||||
return dict(base, **c)
|
||||
|
||||
def evolve(state, corrections):
|
||||
for c in corrections:
|
||||
state, _ = CO.apply(state, c, store=SCHEMAS)
|
||||
return state
|
||||
|
||||
def case(cid, a, citing_c=(), cited_c=(), same_as=()):
|
||||
cc, dc = [corr(c) for c in citing_c], [corr(c) for c in cited_c]
|
||||
res = TR.resolve(a, evolve(citing, cc), evolve(cited, dc), same_as)
|
||||
return {"id": cid, "op": "resolve", "citing": "fixtures/packages/" + WRITER, "cited": "fixtures/packages/" + HALE,
|
||||
"citing_corrections": cc, "cited_corrections": dc, "same_as": list(same_as), "annotation": a,
|
||||
"expect": res}
|
||||
|
||||
def broken(cid, a):
|
||||
# The cited state is given when the passage is in the cited package; a trace that names the draft itself
|
||||
# is checked without it.
|
||||
passage = _anchors_of(a["body"])
|
||||
use = cited if passage and passage[0]["doc"] == HALE else None
|
||||
return {"id": cid, "op": "problems", "citing": "fixtures/packages/" + WRITER,
|
||||
"cited": "fixtures/packages/" + HALE if use else None, "annotation": a,
|
||||
"expect": TR.problems(a, citing, use)}
|
||||
|
||||
unlink = {"op": "relink", "target": sentence_citing, "run": 3, "refs": None, "evidence": person_ev}
|
||||
|
||||
def resolve_to(res):
|
||||
return {"op": "resolve", "target": reference, "resolution": res, "evidence": person_ev}
|
||||
|
||||
def replace(target_id, find, new, ev=person_ev):
|
||||
return {"op": "replace", "target": target_id, "find": find, "replace": new, "evidence": ev}
|
||||
|
||||
hale_doi = "doi:10.5555/example.hale.2026"
|
||||
stale_cited = replace(sentence, "in two steps", "in three stages")
|
||||
stale_citing = replace(sentence_citing, "two-step", "two-stage")
|
||||
cases = [
|
||||
case("current-abstract", best),
|
||||
case("current-table", person),
|
||||
case("current-candidate-with-citation", second),
|
||||
case("changed-passage-moved", best, cited_c=[replace(sentence, "twelve bonds", "all bonds")]),
|
||||
case("changed-sentence", best, citing_c=[replace(sentence_citing, "Its constants", "The constants")]),
|
||||
case("changed-table", person, cited_c=[replace(cell, "553.0", "552.0", crop_ev)]),
|
||||
case("stale-cited", best, cited_c=[stale_cited]),
|
||||
case("stale-citing", best, citing_c=[stale_citing]),
|
||||
case("stale-both", best, citing_c=[stale_citing], cited_c=[stale_cited]),
|
||||
case("stale-citing-whole-sentence", person, citing_c=[stale_citing]),
|
||||
case("unlinked", best, citing_c=[unlink]),
|
||||
case("misresolved-doi", best, citing_c=[resolve_to({"type": "doi", "doi": "10.5555/example.other.2020"})],
|
||||
same_as=[hale_doi]),
|
||||
case("misresolved-document", best, citing_c=[resolve_to({"type": "document", "doc": STORE + "/" + OKAFOR})],
|
||||
same_as=[hale_doi]),
|
||||
case("misresolved-doi-without-same-as", best, citing_c=[resolve_to({"type": "doi", "doi": "10.5555/example.hale.2026"})]),
|
||||
case("current-doi-in-same-as", best, citing_c=[resolve_to({"type": "doi", "doi": "10.5555/Example.Hale.2026"})],
|
||||
same_as=[hale_doi]),
|
||||
case("current-another-version", best, citing_c=[resolve_to({"type": "document", "doc": QHALE + ".v2"})]),
|
||||
case("current-reference-unresolved", best, citing_c=[resolve_to({"type": "none"})]),
|
||||
case("unlinked-before-misresolved", best, citing_c=[unlink, resolve_to({"type": "doi", "doi": "10.5555/example.other.2020"})]),
|
||||
]
|
||||
# Traces that break a rule: each is the confirmed model trace changed in one way.
|
||||
tag_okafor = TAG + "doc:" + STORE + "/" + OKAFOR
|
||||
plain_anchor = _anchors_of(best["body"])[0]
|
||||
cites_nothing = anchor(wdoc, wvals, citing.rev, other_sentence)
|
||||
paragraph = anchor(wdoc, wvals, citing.rev, wdoc.nodes[sentence_citing]["parent"])
|
||||
|
||||
def edited(body_edit=None, target_edit=None):
|
||||
a = copy.deepcopy(best)
|
||||
a["body"].update(body_edit or {})
|
||||
a["target"] = target_edit if target_edit is not None else a["target"]
|
||||
return a
|
||||
|
||||
quote_differs = copy.deepcopy(best["body"]["selector"])
|
||||
quote_differs[1]["exact"] = "in three steps"
|
||||
unknown_node = copy.deepcopy(plain_anchor)
|
||||
unknown_node["node"] = "zzzzzzz"
|
||||
broken_cases = [
|
||||
broken("problems-none", best),
|
||||
broken("problems-table", person),
|
||||
broken("source-is-another-document", edited({"source": tag_okafor})),
|
||||
broken("source-is-the-citing-document", edited({"source": TAG + "doc:" + QWRITER, "selector": selector(span_citing)})),
|
||||
broken("source-without-anchor", edited({"selector": [best["body"]["selector"][1]]})),
|
||||
broken("source-quote-differs", edited({"selector": [best["body"]["selector"][0], quote_differs[1]]})),
|
||||
broken("source-node-unknown", edited({"selector": [{"type": "AnchorSelector", "anchor": unknown_node}]})),
|
||||
broken("target-is-a-paragraph", edited(target_edit=target(paragraph))),
|
||||
broken("target-cites-nothing", edited(target_edit=target(cites_nothing))),
|
||||
broken("target-reference-is-a-sentence", edited({"reference": sentence_citing})),
|
||||
broken("target-is-a-reply", edited(target_edit={"type": "SpecificResource", "source": second["id"]})),
|
||||
]
|
||||
wv(out, "traces.jsonl", cases + broken_cases)
|
||||
|
||||
|
||||
def _anchors_of(body):
|
||||
return [s["anchor"] for s in body["selector"] if s["type"] == "AnchorSelector"]
|
||||
|
||||
|
||||
# --- invalid packages ---------------------------------------------------------------
|
||||
|
||||
|
||||
@@ -1971,13 +2170,14 @@ def main(out):
|
||||
hale = build_hale(out)
|
||||
build_checks_only(out, hale)
|
||||
build_simple(out, "okafor2025open")
|
||||
build_simple(out, "smithlab2027charges")
|
||||
draft = build_simple(out, "smithlab2027charges")
|
||||
tiny = tiny_package(None)
|
||||
tiny_dir = os.path.join(out, "fixtures", "packages", tiny["key"])
|
||||
write(tiny_dir, tiny["files"])
|
||||
# The same package zipped as spec/12 §12.2 says a producer writes a byte-identical archive.
|
||||
write_zip(tiny_dir, tiny_dir + ZIP_EXTENSION)
|
||||
records(out, hale)
|
||||
traces(out, hale, draft)
|
||||
vectors(out, hale, tiny)
|
||||
invalid(out, tiny)
|
||||
|
||||
|
||||
+155
-28
@@ -27,7 +27,7 @@ from paperspec import anchors as AN # noqa: E402
|
||||
from paperspec import corrections as CO # noqa: E402
|
||||
from paperspec import crypto, db, fingerprint as FP, grade as GR, hashing, jcs, receipts as RC, rights as RI # noqa: E402
|
||||
from paperspec import package as PK # noqa: E402
|
||||
from paperspec import keys as K, log as LG, mldsa, targets as TG, vrf # noqa: E402
|
||||
from paperspec import keys as K, log as LG, mldsa, targets as TG, traces as TR, vrf # noqa: E402
|
||||
import unicodedata # noqa: E402
|
||||
from paperspec.model import Doc # noqa: E402
|
||||
from paperspec.notation import fold_math # noqa: E402
|
||||
@@ -77,6 +77,7 @@ def fixture_key(stem):
|
||||
|
||||
|
||||
HALE = fixture_key("hale2026harmonic")
|
||||
DRAFT = fixture_key("smithlab2027charges")
|
||||
TINY_KEY = fixture_key("tiny2026example")
|
||||
SCHEMA_FILES = sorted(f for f in os.listdir(p("schemas")) if f.endswith(".schema.json"))
|
||||
FORMAT_SCHEMA = {f["format"]: f["schema"] for f in js(p("registry", "formats.json"))["formats"]}
|
||||
@@ -194,25 +195,33 @@ def check_records():
|
||||
stage_ids = {js(p("registry", "stages", f))["id"] for f in os.listdir(p("registry", "stages")) if f.endswith(".json")}
|
||||
stages = js(os.path.join(R, "project.json")).get("settings", {}).get("stages")
|
||||
ok("records", stages is None or stages in stage_ids, "project settings.stages names no stage file: %s" % stages)
|
||||
# Confirmed annotations are covered by the layer receipt at their content hash.
|
||||
lr = js(os.path.join(R, "layer", "receipt.json"))
|
||||
items = {(k, i): h for k, i, h in lr["records"]}
|
||||
for a in jl(os.path.join(R, "layer", "annotations.jsonl")):
|
||||
sel = a["target"].get("selector", [])
|
||||
anchors = [x["anchor"] for x in sel if x.get("type") == "AnchorSelector"]
|
||||
for x in sel:
|
||||
if x.get("type") == "TextQuoteSelector":
|
||||
q = anchors[0].get("quote", {}) if anchors else {}
|
||||
same = all(x.get(k, "") == q.get(k, "") for k in ("exact", "prefix", "suffix"))
|
||||
ok("records", "exact" in q and same and set(x) <= {"type", "exact", "prefix", "suffix"},
|
||||
"a TextQuoteSelector must repeat the anchor's plain quote: " + a["id"])
|
||||
body = {k: v for k, v in a.items() if k not in ("status", "receipt", "modified")}
|
||||
ok("records", a["id"].rsplit("/", 1)[1] == record_id(sha256_hex(jcs.dumpb(
|
||||
{k: v for k, v in body.items() if k != "id"}))), "annotation id does not derive: " + a["id"])
|
||||
if a["status"] == "confirmed":
|
||||
ok("records", items.get(("annotation", a["id"].rsplit("/", 1)[1])) == sha256_hex(jcs.dumpb(body)),
|
||||
"a confirmed annotation is not covered at its hash: " + a["id"])
|
||||
ok("records", lr["root"] == crypto.merkle_root(crypto.receipt_items(lr)), "layer receipt root")
|
||||
# Confirmed annotations are covered by the layer receipt at their content hash, in both layers: the example
|
||||
# layer on the main example and the layer of traces on the draft.
|
||||
for layer in ("layer", "traces"):
|
||||
lj = js(os.path.join(R, layer, "layer.json"))
|
||||
lr = js(os.path.join(R, layer, "receipt.json"))
|
||||
items = {(k, i): h for k, i, h in lr["records"]}
|
||||
lines = jl(os.path.join(R, layer, "annotations.jsonl"))
|
||||
ok("records", lj["total"] == len(lines) and all(a["layer"] == lj["id"] for a in lines),
|
||||
"%s: the layer's total or an annotation's layer differs" % layer)
|
||||
ok("records", any(d.endswith("/" + lr["doc"]) for d in lj["docs"]),
|
||||
"%s: the receipt is for a document the layer does not annotate" % layer)
|
||||
for a in lines:
|
||||
sel = a["target"].get("selector", [])
|
||||
anchors = [x["anchor"] for x in sel if x.get("type") == "AnchorSelector"]
|
||||
for x in sel:
|
||||
if x.get("type") == "TextQuoteSelector":
|
||||
q = anchors[0].get("quote", {}) if anchors else {}
|
||||
same = all(x.get(k, "") == q.get(k, "") for k in ("exact", "prefix", "suffix"))
|
||||
ok("records", "exact" in q and same and set(x) <= {"type", "exact", "prefix", "suffix"},
|
||||
"a TextQuoteSelector must repeat the anchor's plain quote: " + a["id"])
|
||||
body = {k: v for k, v in a.items() if k not in ("status", "receipt", "modified")}
|
||||
ok("records", a["id"].rsplit("/", 1)[1] == record_id(sha256_hex(jcs.dumpb(
|
||||
{k: v for k, v in body.items() if k != "id"}))), "annotation id does not derive: " + a["id"])
|
||||
if a["status"] == "confirmed":
|
||||
ok("records", items.get(("annotation", a["id"].rsplit("/", 1)[1])) == sha256_hex(jcs.dumpb(body)),
|
||||
"a confirmed annotation is not covered at its hash: " + a["id"])
|
||||
ok("records", lr["root"] == crypto.merkle_root(crypto.receipt_items(lr)), "%s: layer receipt root" % layer)
|
||||
# Every figure of the main example has a confirmed description, which is what an accessible export needs.
|
||||
anns = jl(os.path.join(R, "layer", "annotations.jsonl"))
|
||||
described = {x["anchor"]["node"] for a in anns if a["body"]["kind"] == "description" and a["status"] == "confirmed"
|
||||
@@ -838,6 +847,116 @@ def check_vectors():
|
||||
ok("vectors", sorted(LG.tiles(c["entries"])) == c["tiles"], "checkpoint %s: tile paths" % c["id"])
|
||||
|
||||
|
||||
def check_traces():
|
||||
"""Traced citations (spec/18 §18.5): the layer of traces on the draft, resolved against the two packages, and
|
||||
the vectors, which resolve and check traces after corrections that bring every outcome and every refusal."""
|
||||
R = p("fixtures", "records", "traces")
|
||||
states = {}
|
||||
|
||||
def st(path):
|
||||
return states.setdefault(path, state_of(p(path)))
|
||||
|
||||
def evolve(state, corrections):
|
||||
for c in corrections:
|
||||
state, _ = CO.apply(state, c, store=STORE)
|
||||
return state
|
||||
|
||||
rules = {r["id"]: r for r in js(p("registry", "rules.json"))["rules"]}
|
||||
for rule in ("layer.trace.source", "layer.trace.target"):
|
||||
ok("traces", rule in rules and rules[rule]["chapter"] == "spec/18-layers.md", "%s is not registered" % rule)
|
||||
|
||||
# The fixture layer: a model's confirmed trace to the abstract, a candidate a person rejected, a person's trace to
|
||||
# Table 1 and a reply that disputes the first. Every trace is current, and breaks no rule alone or with the states.
|
||||
citing, cited = st("fixtures/packages/" + DRAFT), st("fixtures/packages/" + HALE)
|
||||
same_as = js(p("fixtures", "packages", HALE, "document.json"))["work"]["same_as"]
|
||||
anns = jl(os.path.join(R, "annotations.jsonl"))
|
||||
mine = [a for a in anns if a["body"]["kind"] == "trace"]
|
||||
ok("traces", len(mine) == 3 and len(anns) == 4, "the layer holds three traces and a reply")
|
||||
refs = {r["node"]: r["resolution"] for r in citing.references}
|
||||
for a in mine:
|
||||
b = a["body"]
|
||||
ok("traces", TR.problems(a) == [] and TR.problems(a, citing, cited) == [], "a trace breaks a rule: " + a["id"])
|
||||
got = TR.resolve(a, citing, cited, same_as)
|
||||
ok("traces", got["result"] == "current" and got["open"] is not None, "a fixture trace is not current: " + a["id"])
|
||||
ok("traces", ("proposer" in b) == (a["creator"]["type"] == "Software"), "proposer iff Software: " + a["id"])
|
||||
ok("traces", refs[b["reference"]] == {"type": "document", "doc": b["source"].split("doc:", 1)[1]},
|
||||
"the reference does not resolve to the trace's source: " + a["id"])
|
||||
tool = next(a for a in mine if a["creator"]["type"] == "Software" and a["status"] == "confirmed")
|
||||
ok("traces", {a["status"] for a in mine} == {"confirmed", "rejected"}
|
||||
and [a["status"] for a in mine if a["creator"]["type"] == "Software"].count("rejected") == 1
|
||||
and any(a["creator"]["type"] == "Person" and a["status"] == "confirmed"
|
||||
and TR._anchors(a["body"])[0]["kind"] == "table" for a in mine),
|
||||
"the layer lacks a confirmed model trace, a rejected candidate or a person's trace to a table")
|
||||
ok("traces", [a["id"] for a in mine if TR.disputed(a["id"], anns)] == [tool["id"]]
|
||||
and len(TR.disputes(tool["id"], anns)) == 1, "only the model's confirmed trace is disputed")
|
||||
reply = TR.disputes(tool["id"], anns)[0]
|
||||
ok("traces", reply["motivation"] == "assessing" and reply["creator"] != tool["creator"] and reply not in mine,
|
||||
"the dispute is not an assessing reply by another")
|
||||
for edit in ({"status": "withdrawn"}, {"status": "rejected"}, {"motivation": "replying"}):
|
||||
ok("traces", not TR.disputed(tool["id"], [dict(reply, **edit)]), "a reply with %s disputes" % edit)
|
||||
ok("traces", TR.replies(tool["id"], anns) == [reply] and TR.replies(reply["id"], anns) == [], "replies are found by target")
|
||||
# A model's trace names its proposer; a person's may too; the shapes are closed (spec/18 §18.4).
|
||||
bare = {k: v for k, v in tool["body"].items() if k != "proposer"}
|
||||
person = next(a for a in mine if a["creator"]["type"] == "Person")
|
||||
for name, a, good in (
|
||||
("a model's trace without a proposer", dict(tool, body=bare), False),
|
||||
("a person's trace with one", dict(person, body=dict(person["body"], proposer=tool["body"]["proposer"])), True),
|
||||
("a rank below 1", dict(tool, body=dict(tool["body"], proposer=dict(tool["body"]["proposer"], rank=0))), False),
|
||||
("a score that is no decimal", dict(tool, body=dict(tool["body"], proposer=dict(tool["body"]["proposer"], score="high"))), False),
|
||||
("an unknown verdict", dict(person, body=dict(person["body"], verdict="agrees")), False),
|
||||
("no reference", dict(person, body={k: v for k, v in person["body"].items() if k != "reference"}), False),
|
||||
("a source that is no document tag", dict(person, body=dict(person["body"], source="hale2026harmonic-damb")), False),
|
||||
("an empty note", dict(person, body=dict(person["body"], note="")), False),
|
||||
("an extra property", dict(person, body=dict(person["body"], language="en")), False)):
|
||||
ok("traces", (schema_errors("annotation.schema.json", a) == []) == good, "schema, %s" % name)
|
||||
# The states must be those of the documents the trace names.
|
||||
for call in (lambda: TR.resolve(tool, cited, citing), lambda: TR.problems(tool, cited), lambda: TR.problems(tool, citing, citing),
|
||||
lambda: TR.resolve(next(a for a in jl(os.path.join(ROOT, "fixtures", "records", "layer", "annotations.jsonl"))), citing, cited)):
|
||||
try:
|
||||
call()
|
||||
raised = False
|
||||
except ValueError:
|
||||
raised = True
|
||||
ok("traces", raised, "a call with the wrong states or a body that is no trace did not raise")
|
||||
# The vocabulary and the store know the kind (spec/18 §18.4, sql/store.sql).
|
||||
ctx = js(p("context", "scienting-1.jsonld"))["@context"]
|
||||
ok("traces", ctx["note"] == "sct:note" and ctx["proposer"] == {"@id": "sct:proposedBy", "@type": "@json"},
|
||||
"the context lacks the terms note and proposer")
|
||||
|
||||
# The vectors.
|
||||
outcomes, broken = set(), set()
|
||||
ids = set()
|
||||
for c in V("traces.jsonl"):
|
||||
ids.add(c["id"])
|
||||
valid("traces", "annotation.schema.json", c["annotation"], "vector " + c["id"])
|
||||
a_citing = st(c["citing"])
|
||||
snapshot = json.dumps([a_citing.nodes, a_citing.references], sort_keys=True)
|
||||
if c["op"] == "resolve":
|
||||
got = TR.resolve(c["annotation"], evolve(a_citing, c["citing_corrections"]),
|
||||
evolve(st(c["cited"]), c["cited_corrections"]), c["same_as"])
|
||||
ok("traces", got == c["expect"], "traces " + c["id"])
|
||||
ok("traces", got["result"] in TR.RESULTS and ("side" in got) == (got["result"] == "stale")
|
||||
and ("reason" in got) == (got["result"] != "current")
|
||||
and (got["open"] is None) == (got["cited"]["result"] == "stale") and (got["cites"] == [] or got["open"]),
|
||||
"traces %s: the result is not shaped as traces.resolve says" % c["id"])
|
||||
outcomes.add(got["result"])
|
||||
# Correcting a state to judge a trace leaves the state it started from as it was.
|
||||
ok("traces", json.dumps([a_citing.nodes, a_citing.references], sort_keys=True) == snapshot,
|
||||
"traces %s: the state changed" % c["id"])
|
||||
else:
|
||||
got = TR.problems(c["annotation"], a_citing, st(c["cited"]) if c["cited"] else None)
|
||||
ok("traces", got == c["expect"], "traces " + c["id"])
|
||||
broken |= {x["rule"] for x in got}
|
||||
ok("traces", all(x["rule"] in rules for x in got), "traces %s: an unregistered rule" % c["id"])
|
||||
ok("traces", outcomes == set(TR.RESULTS), "every outcome occurs: %s" % sorted(outcomes))
|
||||
ok("traces", broken == {"layer.trace.source", "layer.trace.target"}, "both rules are broken by some case")
|
||||
ok("traces", {"changed-passage-moved", "stale-both", "unlinked", "misresolved-doi-without-same-as",
|
||||
"current-doi-in-same-as"} <= ids, "the vectors lack a case they are known for")
|
||||
cites = [c for c in V("traces.jsonl") if c["op"] == "resolve" and c["expect"]["cites"]]
|
||||
ok("traces", len(cites) == 1 and cites[0]["expect"]["cites"][0]["refs"][0]["resolution"]["type"] == "doi",
|
||||
"one case follows a citation inside the passage")
|
||||
|
||||
|
||||
def check_determinism():
|
||||
with tempfile.TemporaryDirectory() as tmp:
|
||||
subprocess.run([sys.executable, "-m", "paperspec", "build", "--out", tmp], check=True)
|
||||
@@ -895,6 +1014,10 @@ def check_sql():
|
||||
for m in re.finditer(r"CREATE INDEX (\w+) ON", ddl):
|
||||
ok("sql", m.group(1).upper() not in RESERVED, "index name " + m.group(1))
|
||||
ok("sql", "FOREIGN KEY" not in ddl and "REFERENCES" not in ddl, "no foreign keys")
|
||||
# The store accepts exactly the body kinds the annotation schema has.
|
||||
sql_kinds = set(re.findall(r"'([a-z-]+)'", re.search(r"body_kind VARCHAR\(24\) NOT NULL CHECK \(body_kind IN \((.*?)\)\)", ddl).group(1)))
|
||||
schema_kinds = {b["properties"]["kind"]["const"] for b in STORE.load("annotation.schema.json")["properties"]["body"]["oneOf"]}
|
||||
ok("sql", sql_kinds == schema_kinds, "body_kind differs from the schema's body kinds: %s" % sorted(sql_kinds ^ schema_kinds))
|
||||
with tempfile.TemporaryDirectory() as tmp:
|
||||
conn = sqlite3.connect(os.path.join(tmp, "bundle.db"))
|
||||
db.create_bundle(conn, p("sql"))
|
||||
@@ -921,14 +1044,17 @@ def check_sql():
|
||||
check_bundle(conn)
|
||||
# Store records satisfy the store tables' CHECK constraints.
|
||||
R = p("fixtures", "records")
|
||||
lay = js(os.path.join(R, "layer", "layer.json"))
|
||||
conn.execute("INSERT INTO layers VALUES (?,?,?,?,?,?,?,?)",
|
||||
(lay["id"], lay.get("project"), lay["label"], lay["purpose"], lay.get("audience"),
|
||||
lay["visibility"], jcs.dumps(lay["license"]), jcs.dumps(lay)))
|
||||
for a in jl(os.path.join(R, "layer", "annotations.jsonl")):
|
||||
conn.execute("INSERT INTO annotations VALUES (?,?,?,?,?,?,?,?,?)",
|
||||
(a["id"], a["layer"], a["body"]["kind"], a["body"].get("category"), a["status"], a.get("receipt"),
|
||||
a["creator"]["id"], a["created"], jcs.dumps(a)))
|
||||
for layer in ("layer", "traces"):
|
||||
lay = js(os.path.join(R, layer, "layer.json"))
|
||||
conn.execute("INSERT INTO layers VALUES (?,?,?,?,?,?,?,?)",
|
||||
(lay["id"], lay.get("project"), lay["label"], lay["purpose"], lay.get("audience"),
|
||||
lay["visibility"], jcs.dumps(lay["license"]), jcs.dumps(lay)))
|
||||
for a in jl(os.path.join(R, layer, "annotations.jsonl")):
|
||||
conn.execute("INSERT INTO annotations VALUES (?,?,?,?,?,?,?,?,?)",
|
||||
(a["id"], a["layer"], a["body"]["kind"], a["body"].get("category"), a["status"],
|
||||
a.get("receipt"), a["creator"]["id"], a["created"], jcs.dumps(a)))
|
||||
kinds = {k for (k,) in conn.execute("SELECT DISTINCT body_kind FROM annotations")}
|
||||
ok("sql", "trace" in kinds, "the store holds no trace annotation")
|
||||
prj = js(os.path.join(R, "project.json"))
|
||||
conn.execute("INSERT INTO projects VALUES (?,?,?,?,?,?,?)",
|
||||
(prj["key"], prj.get("store"), prj["name"], prj.get("description"), jcs.dumps(prj.get("claims")),
|
||||
@@ -1319,6 +1445,7 @@ def run(args):
|
||||
check_gate()
|
||||
check_invalid()
|
||||
check_vectors()
|
||||
check_traces()
|
||||
if not args.quick:
|
||||
check_determinism()
|
||||
check_sql()
|
||||
|
||||
@@ -0,0 +1,268 @@
|
||||
"""Traced citations: where a citation leads, and whether it still does.
|
||||
|
||||
A trace is an annotation whose body has the kind `trace`: its target is a sentence of the citing document, and its
|
||||
body names the reference the sentence cites and a passage of the cited document. Non-normative; see
|
||||
spec/18-layers.md §18.4 for the body and spec/22-conformance.md §22.4 for the rules `layer.trace.source` and
|
||||
`layer.trace.target`.
|
||||
|
||||
`resolve` judges a trace against the current states of both documents, `problems` checks a trace when it is
|
||||
written, and `disputes` finds the replies that dispute it. Nothing here needs more than the standard library.
|
||||
"""
|
||||
|
||||
import re
|
||||
|
||||
from . import anchors as AN
|
||||
from . import hashing
|
||||
from .model import RUN_KINDS
|
||||
|
||||
RESULTS = ("current", "changed", "stale", "unlinked", "misresolved")
|
||||
DOC_TAG = re.compile(r"^tag:scient\.ing,2026:doc:(?P<store>[a-z0-9][a-z0-9._-]*)/(?P<key>[a-z][a-z0-9]*-[0-9a-hjkmnp-tv-z]{4,51}"
|
||||
r"(?:\.[a-z0-9][a-z0-9-]*)*)$")
|
||||
# The statuses of a reply that still stands: a withdrawn or rejected reply disputes nothing.
|
||||
LIVE = ("proposed", "confirmed")
|
||||
|
||||
|
||||
def doc_tag(store, key):
|
||||
"""The tag of a document of a store, as `source` and a target's source write it."""
|
||||
return "tag:scient.ing,2026:doc:%s/%s" % (store, key)
|
||||
|
||||
|
||||
def _anchors(holder):
|
||||
"""The anchors of the `AnchorSelector`s of a body or a target, in order."""
|
||||
sel = holder.get("selector") if isinstance(holder, dict) else None
|
||||
return [s["anchor"] for s in sel or [] if isinstance(s, dict) and s.get("type") == "AnchorSelector"]
|
||||
|
||||
|
||||
def _bare(doc):
|
||||
"""A document key without its store: `smithlab/hale2026harmonic-damb` and `hale2026harmonic-damb` agree."""
|
||||
return doc.rsplit("/", 1)[-1]
|
||||
|
||||
|
||||
def _work(doc):
|
||||
"""The work key of a document key: the key before the first dot of its version suffix (spec/02 §2.4)."""
|
||||
return _bare(doc).split(".", 1)[0]
|
||||
|
||||
|
||||
def _live(state, nid):
|
||||
"""The live node an ID leads to, following aliases and retirements; None when it was deleted or never was."""
|
||||
return AN._follow(state, nid)[0]
|
||||
|
||||
|
||||
def _record(state, nid):
|
||||
"""The resolution of a reference: the record of references.jsonl for the node, else `none` (spec/10)."""
|
||||
for r in state.references:
|
||||
if r["node"] == nid:
|
||||
return r["resolution"]
|
||||
return {"type": "none"}
|
||||
|
||||
|
||||
def _cites(state, nid, reference):
|
||||
"""Whether the leaf `nid` has a cite run that names the reference `reference`, however either was renamed."""
|
||||
n = {x["id"]: x for x in state.nodes}.get(nid)
|
||||
if n is None or n["kind"] not in RUN_KINDS:
|
||||
return False
|
||||
want = _live(state, reference)
|
||||
return want is not None and any(_live(state, x) == want for r in n["content"] if r["kind"] == "cite"
|
||||
for x in r["refs"])
|
||||
|
||||
|
||||
def _agrees(resolution, cited_key, same_as):
|
||||
"""Whether a reference's resolution still names the cited work.
|
||||
|
||||
`none` names nothing, so it contradicts nothing. A `document` resolution agrees when its key is the cited
|
||||
document's, another version of the same work (the key before the first dot), or one of `same_as`; a `doi`
|
||||
resolution agrees when `doi:` and the DOI, in lower case, is one of `same_as`.
|
||||
"""
|
||||
known = {x.lower() for x in same_as}
|
||||
kind = resolution.get("type")
|
||||
if kind == "document":
|
||||
doc = resolution["doc"]
|
||||
return _work(doc) == _work(cited_key) or _bare(doc) in known or doc in known
|
||||
if kind == "doi":
|
||||
return "doi:" + resolution["doi"].lower() in known
|
||||
return True
|
||||
|
||||
|
||||
def _passage_cites(state, node, span):
|
||||
"""The citations inside a passage, so a chain of 'as previously reported' can be followed one trace at a time.
|
||||
|
||||
One item for each `cite` run of the passage's leaves (those that overlap `span`, for a span of a leaf), in
|
||||
document order: the leaf, the run's index and span in the leaf's plain projection, its printed text, and each
|
||||
reference it names with that reference's resolution in the cited document.
|
||||
"""
|
||||
doc = state.doc()
|
||||
leaves = doc.leaves(node)
|
||||
out = []
|
||||
for leaf in leaves:
|
||||
n = doc.nodes[leaf]
|
||||
if n["kind"] not in RUN_KINDS:
|
||||
continue
|
||||
pos = 0
|
||||
for i, r in enumerate(n["content"]):
|
||||
end = pos + len(hashing.plain_runs([r], doc))
|
||||
if r["kind"] == "cite" and (span is None or (pos < span[1] and end > span[0])):
|
||||
out.append({"node": leaf, "run": i, "span": [pos, end], "text": r["text"],
|
||||
"refs": [{"node": x, "resolution": _record(state, _live(state, x) or x)} for x in r["refs"]]})
|
||||
pos = end
|
||||
return out
|
||||
|
||||
|
||||
def _sides(annotation, citing_state, cited_state):
|
||||
"""The citing anchor and the passage anchor of a trace, after checking that the states are those documents'."""
|
||||
body = annotation.get("body", {})
|
||||
if body.get("kind") != "trace":
|
||||
raise ValueError("the annotation is not a trace")
|
||||
mine, theirs = _anchors(annotation["target"]), _anchors(body)
|
||||
if not mine or not theirs:
|
||||
raise ValueError("a trace needs an AnchorSelector on its target and one in its body")
|
||||
if mine[0]["doc"] != citing_state.key:
|
||||
raise ValueError("citing_state is %s, but the trace's target is on %s" % (citing_state.key, mine[0]["doc"]))
|
||||
if theirs[0]["doc"] != cited_state.key:
|
||||
raise ValueError("cited_state is %s, but the trace's passage is in %s" % (cited_state.key, theirs[0]["doc"]))
|
||||
return mine[0], theirs[0]
|
||||
|
||||
|
||||
def resolve(annotation, citing_state, cited_state, same_as=()):
|
||||
"""Judge a trace against the current state of the citing document and of the cited document.
|
||||
|
||||
`annotation` is the trace, an annotation with a `trace` body; `citing_state` and `cited_state` are the
|
||||
`corrections.State` of the document its target is on and of the document its body's `source` names (for a
|
||||
package, `package.read_state`); `same_as` is everything the cited work is known by, the `work.same_as` of its
|
||||
`document.json` (`doi:…`) and the keys of its other versions, and decides whether a `doi` resolution of the
|
||||
reference still names the cited work. Both anchors are resolved by spec/06 §6.5.
|
||||
|
||||
Returns a dict. `result` is the first that applies of:
|
||||
|
||||
- `stale`: an anchor does not resolve; `side` is `citing`, `cited` or `both`.
|
||||
- `unlinked`: the sentence no longer cites the reference (a `relink` removed the citation, or the reference
|
||||
was deleted).
|
||||
- `misresolved`: the reference now resolves to a `doi` or a `document` that is not the cited work (a `resolve`).
|
||||
- `changed`: both anchors resolve, and at least one is not `current`, so the passage or the sentence is not as
|
||||
it was when the trace was made; a reader shows the trace with its new address and offers a review.
|
||||
- `current`: both anchors are `current` and the citation links the reference to the cited work.
|
||||
|
||||
`citing` and `cited` are the two anchor resolutions (spec/06 §6.5). `open` is the current address of the
|
||||
cited passage, `store/key#node@pin[start:end]`, whenever its anchor still resolves, whatever `result` says;
|
||||
`None` when it is stale. `cites` lists the citations inside the passage, `[]` when it is stale: one item
|
||||
`{node, run, span, text, refs}` for each `cite` run of the passage's leaves, in document order, where `node`
|
||||
and `run` find the run in the cited document, `span` is its place in the leaf's plain projection, `text` is
|
||||
what the paper prints, and `refs` lists `{node, resolution}` for each reference it names, so a chain of "as
|
||||
previously reported" is followed by looking for a trace on that leaf and reference. `reference` is the live
|
||||
node of the reference in the citing document, or None when it was deleted, and `resolution` the one its
|
||||
record holds. `reason` says why for every result but `current`.
|
||||
|
||||
The annotation's `status` and its replies are not read here: a rejected candidate resolves like any other
|
||||
trace, and `disputed` says whether a reply disputes it. A `ValueError` means the call is wrong, not the trace:
|
||||
the annotation has no trace body, or a state is not that of the document the annotation names.
|
||||
"""
|
||||
body = annotation["body"]
|
||||
mine, theirs = _sides(annotation, citing_state, cited_state)
|
||||
citing, cited = AN.resolve(citing_state, mine), AN.resolve(cited_state, theirs)
|
||||
reference = _live(citing_state, body["reference"])
|
||||
resolution = _record(citing_state, reference) if reference else _record(citing_state, body["reference"])
|
||||
out = {"citing": citing, "cited": cited, "reference": reference, "resolution": resolution}
|
||||
if cited["result"] != "stale":
|
||||
m = DOC_TAG.match(body["source"])
|
||||
store = m.group("store") if m else theirs.get("store")
|
||||
out["open"] = AN.format_address(store, cited_state.key, cited["node"], cited["node_hash"][:6], cited.get("span"))
|
||||
out["cites"] = _passage_cites(cited_state, cited["node"], cited.get("span"))
|
||||
else:
|
||||
out["open"], out["cites"] = None, []
|
||||
stale = [s for s, r in (("citing", citing), ("cited", cited)) if r["result"] == "stale"]
|
||||
if stale:
|
||||
out.update(result="stale", side="both" if len(stale) == 2 else stale[0],
|
||||
reason="; ".join("the %s anchor is stale: %s" % (s, (citing if s == "citing" else cited).get("reason", ""))
|
||||
for s in stale))
|
||||
elif reference is None or not _cites(citing_state, citing["node"], reference):
|
||||
out.update(result="unlinked", reason="the sentence no longer cites the reference")
|
||||
elif not _agrees(resolution, cited_state.key, same_as):
|
||||
out.update(result="misresolved", reason="the reference resolves to %s, not to the cited work"
|
||||
% (resolution.get("doc") or "doi:" + resolution.get("doi", "")))
|
||||
elif citing["result"] != "current" or cited["result"] != "current":
|
||||
out.update(result="changed", reason="; ".join("the %s anchor is %s" % (s, r["result"]) for s, r in (
|
||||
("citing", citing), ("cited", cited)) if r["result"] != "current"))
|
||||
else:
|
||||
out["result"] = "current"
|
||||
return out
|
||||
|
||||
|
||||
def problems(annotation, citing_state=None, cited_state=None):
|
||||
"""The rules a trace breaks, as `[{"rule", "message"}]` in the order checked; empty when it breaks none.
|
||||
|
||||
Without states the trace is checked alone; with them, against the documents it was made on, which is how a
|
||||
producer checks a trace it writes or imports. A trace later made `unlinked` by a correction breaks
|
||||
`layer.trace.target` here, since the sentence it names no longer cites the reference; `resolve` is the judgment
|
||||
of a trace over time. `layer.trace.source` covers the body, `layer.trace.target` the target and the reference.
|
||||
"""
|
||||
found = []
|
||||
|
||||
def add(rule, message):
|
||||
found.append({"rule": rule, "message": message})
|
||||
|
||||
body, target = annotation["body"], annotation["target"]
|
||||
mine, passage = _anchors(target), _anchors(body)
|
||||
if citing_state is not None and mine and mine[0]["doc"] != citing_state.key:
|
||||
raise ValueError("citing_state is not the document the trace's target is on")
|
||||
if cited_state is not None and passage and passage[0]["doc"] != cited_state.key:
|
||||
raise ValueError("cited_state is not the document the trace's passage is in")
|
||||
|
||||
# The source: a document of a store, named by its tag, and the passage inside it.
|
||||
m = DOC_TAG.match(body.get("source", ""))
|
||||
if not m:
|
||||
add("layer.trace.source", "the source is not a document tag")
|
||||
if not passage:
|
||||
add("layer.trace.source", "the body's selector has no AnchorSelector")
|
||||
elif m:
|
||||
for a in passage:
|
||||
if a["doc"] != m.group("key") or ("store" in a and a["store"] != m.group("store")):
|
||||
add("layer.trace.source", "an anchor of the selector is in %s, not in the source %s/%s"
|
||||
% (a["doc"], m.group("store"), m.group("key")))
|
||||
break
|
||||
citing_doc = target.get("source", "") if isinstance(target, dict) else ""
|
||||
if citing_doc == body["source"]:
|
||||
add("layer.trace.source", "the source is the citing document itself; a trace leads to another paper")
|
||||
quote = passage[0].get("quote", {}) if passage else {}
|
||||
for s in body.get("selector", []) if passage else []:
|
||||
if isinstance(s, dict) and s.get("type") == "TextQuoteSelector":
|
||||
if "exact" not in quote or any(s.get(k, "") != quote.get(k, "") for k in ("exact", "prefix", "suffix")):
|
||||
add("layer.trace.source", "the TextQuoteSelector does not repeat the anchor's plain quote")
|
||||
break
|
||||
if cited_state is not None and passage:
|
||||
node = passage[0]["node"]
|
||||
if node not in {n["id"] for n in cited_state.nodes} | {e["id"] for e in cited_state.ids}:
|
||||
add("layer.trace.source", "the passage's node %s is not in the cited document" % node)
|
||||
|
||||
# The target: a leaf with runs of the citing document, which cites the reference.
|
||||
if not mine:
|
||||
add("layer.trace.target", "the target is not a document selected by an AnchorSelector")
|
||||
else:
|
||||
a = mine[0]
|
||||
kind = a.get("kind")
|
||||
if (kind is not None and kind not in RUN_KINDS) or "quote" not in a:
|
||||
add("layer.trace.target", "the target anchor is not on a leaf with runs, whole or in part")
|
||||
elif citing_state is not None:
|
||||
live = _live(citing_state, a["node"])
|
||||
nodes = {n["id"]: n for n in citing_state.nodes}
|
||||
ref = nodes.get(_live(citing_state, body["reference"]) or "")
|
||||
if ref is None or ref["kind"] != "reference":
|
||||
add("layer.trace.target", "the reference %s is not a reference node of the citing document" % body["reference"])
|
||||
elif live is None or not _cites(citing_state, live, body["reference"]):
|
||||
add("layer.trace.target", "the sentence does not cite the reference %s" % body["reference"])
|
||||
return found
|
||||
|
||||
|
||||
def replies(annotation_id, annotations):
|
||||
"""The annotations among `annotations` that reply to the annotation `annotation_id`: those whose target is it."""
|
||||
return [a for a in annotations if isinstance(a.get("target"), dict) and "selector" not in a["target"]
|
||||
and a["target"].get("source") == annotation_id]
|
||||
|
||||
|
||||
def disputes(annotation_id, annotations):
|
||||
"""The replies that dispute a trace: those with the motivation `assessing` that are `proposed` or `confirmed`."""
|
||||
return [a for a in replies(annotation_id, annotations) if a.get("motivation") == "assessing"
|
||||
and a.get("status") in LIVE]
|
||||
|
||||
|
||||
def disputed(annotation_id, annotations):
|
||||
"""Whether a trace is disputed; a dispute is computed from the replies, never stored on the trace."""
|
||||
return bool(disputes(annotation_id, annotations))
|
||||
+2
-2
@@ -75,8 +75,8 @@ A node's five values depend only on its own subtree, so after a correction the o
|
||||
`paperspec check` compares the result after every correction with a slow computation that projects each node from its whole subtree.
|
||||
|
||||
Other repositories of the project do not reimplement the specification: they install this repository as the Python package `paperspec`, from the Gitea package registry or as a git pin ([deploy/](../deploy/README.md#install-from-the-registry)), and import the oracle from it, so that every key, identifier, canonical JSON string, package check and database form comes from one piece of code.
|
||||
The library surface they use is `keys`, `text`, `jcs`, `package`, `db`, `schema` and `rights`, with the schemas, SQL and vectors as package data: `paperspec.resources.path("sql")` finds a folder in an installed wheel and in an editable install alike.
|
||||
The vectors travel with the package, but `resolve.jsonl`, `grading.jsonl` and `targets.jsonl` name a fixture package, and the fixtures stay in the repository.
|
||||
The library surface they use is `keys`, `text`, `jcs`, `package`, `db`, `schema`, `rights` and `traces`, with the schemas, SQL and vectors as package data: `paperspec.resources.path("sql")` finds a folder in an installed wheel and in an editable install alike.
|
||||
The vectors travel with the package, but `resolve.jsonl`, `grading.jsonl`, `targets.jsonl` and `traces.jsonl` name a fixture package, and the fixtures stay in the repository.
|
||||
The package needs Python 3.14, the only version with the Unicode 16.0 character data the specification pins, and refuses to import on any other.
|
||||
Importing any of its modules needs only the standard library.
|
||||
Schema validation (`schema`, and with it `package.validate` and `corrections.gate`) needs `paperspec[schema]` (`jsonschema` and `referencing`), and HKDF and Ed25519 in `crypto`, which the signature and receipt checks use, need `paperspec[crypto]`.
|
||||
|
||||
+7
-3
@@ -52,7 +52,7 @@ The procedure is the same for every file:
|
||||
3.
|
||||
Find the inputs and the expected outputs of the case.
|
||||
The table below says which fields are which, and the chapter it links defines the rule.
|
||||
Only six files, `alignment`, `corrections`, `grading`, `resolve`, `rights` and `targets`, put the expected outputs under `expect`.
|
||||
Only seven files, `alignment`, `corrections`, `grading`, `resolve`, `rights`, `targets` and `traces`, put the expected outputs under `expect`.
|
||||
In the other nineteen the expected outputs are fields beside the inputs, such as `output`, `sha256`, `canonical` or `parsed`.
|
||||
4.
|
||||
Compute each output from the inputs with your implementation.
|
||||
@@ -74,11 +74,14 @@ Some files change the procedure:
|
||||
- `fingerprints.jsonl` needs the download-stamp rules in [registry/stamps/scienting-stamps-1.json](../registry/stamps/scienting-stamps-1.json), which each case names in `rules`.
|
||||
- `resolve.jsonl`, `grading.jsonl` and `targets.jsonl` name a package by a path from the repository root.
|
||||
All three use `fixtures/packages/hale2026harmonic-damb`.
|
||||
- `traces.jsonl` names two packages, `citing` (the draft) and `cited` (the main example), and for a `resolve` case lists the corrections to apply to each, in order, before judging the trace, and the `same_as` identifiers of the cited work.
|
||||
Apply them with the gate ([11.4](../spec/11-corrections.md#114-the-gate)) to the package's state at its own revision.
|
||||
A `problems` case gives the packages' states as they are, and `cited` is null when the trace names no passage in the main example.
|
||||
|
||||
Several files also mix more than one kind of case, told apart by their fields.
|
||||
`ids.jsonl` has minting cases and letter-first cases, `keyed.jsonl` and `signatures.jsonl` have a case for each derivation or record, `spotcheck.jsonl` has sample-size cases and one full sample, and `rights.jsonl` has stage checks (a `stage` field), backend checks (a `backend` field) and one combining case (neither).
|
||||
|
||||
The reference checker, `paperspec check` ([tools/](../tools/README.md)), runs all twenty-five files, together with the rest of the repository's checks:
|
||||
The reference checker, `paperspec check` ([tools/](../tools/README.md)), runs all twenty-six files, together with the rest of the repository's checks:
|
||||
|
||||
```sh
|
||||
paperspec check --quick
|
||||
@@ -120,6 +123,7 @@ The table below uses a few terms that the chapters define:
|
||||
| `alignment.jsonl` | Two readings of a document, their pairs and their suspects. | [5.6](../spec/05-hashes.md#56-alignment) |
|
||||
| `corrections.jsonl` | Each operation and refusal on a small document: the state before, the correction, and the record and state after, or the rule broken; with the reference records for a `resolve`, and the leaf as printed for an `emend`. | [11](../spec/11-corrections.md) |
|
||||
| `resolve.jsonl` | Anchors made on the example paper's first reading, and how each resolves after its corrections. | [6.5](../spec/06-anchors.md#65-resolution) |
|
||||
| `traces.jsonl` | Traced citations: a trace on the draft's sentence that cites the main example, with the corrections that make it current, changed, stale, unlinked or misresolved, the passage's current address and the citations inside it (`op` `resolve`); and traces that break `layer.trace.source` or `layer.trace.target` (`op` `problems`). | [18.5](../spec/18-layers.md#185-traced-citations) |
|
||||
| `rights.jsonl` | Stage checks with policies and expiry, the combining rule, and backend conditions. | [14](../spec/14-rights.md) |
|
||||
| `grading.jsonl` | Exercises, submissions and the grades they earn. | [19.4](../spec/19-exercises.md#194-grading) |
|
||||
| `targets.jsonl` | The target set of targeted F3 for the example paper under each digit rule, given the anchored nodes. | [8.10](../spec/08-fidelity.md#810-targeted-f3) |
|
||||
@@ -138,7 +142,7 @@ A candidate format changes only with a recorded reason and a new or changed vect
|
||||
|
||||
A passing run shows one thing: for these inputs, your implementation computes the same values as the reference implementation in `src/paperspec/`.
|
||||
That code computed the expected outputs, and `pixi run build` regenerates them ([tools/](../tools/README.md)).
|
||||
The vectors hold 330 cases in all, some of them small: `hashes.jsonl` has two documents, `alignment.jsonl` two pairs of readings, `offsets.jsonl` two cases.
|
||||
The vectors hold 359 cases in all, some of them small: `hashes.jsonl` has two documents, `alignment.jsonl` two pairs of readings, `offsets.jsonl` two cases.
|
||||
|
||||
The vectors do not show the following.
|
||||
|
||||
|
||||
@@ -0,0 +1,29 @@
|
||||
{"annotation":{"@context":["http://www.w3.org/ns/anno.jsonld","https://paperspec.scient.ing/context/scienting-1.jsonld"],"body":{"kind":"trace","note":"The abstract states the two steps: a scan first, then vibrational frequencies.","proposer":{"model":{"name":"example-model","parameters":"998b5161e39e01c8bc43ef81ec89141e15b7ea53b2e936707920a6097dbcb2c5","processor":"local","prompt":"9cc338fba7c62db7266f530cb555a5ee36910d41a0120a3fe7fd7de1944d0931","provider":"Example Models (illustrative)","version":"2026-09","weights":"504b2d340045b92c22682b60020c21103aae7ff41172bb632fbe305eb4af9b5e"},"rank":1,"run":"illustrative-run-2","score":"0.8731"},"reference":"ebcq5cy","selector":[{"anchor":{"doc":"hale2026harmonic-damb","format":"scienting-anchor/1","kind":"sentence","node":"mhp0at8","node_hash":"ee42c4780724a411697e3a95daf182d37c792d4689a7172f32929785d5fd968e","own_hash":"24cc437784bf358d639755dbf59984ad2c0c7a0e7f9448b23e965039b67f759b","quote":{"exact":"in two steps","prefix":"bond constants for twelve bonds ","suffix":", first to a scan and then to vi"},"rev":12,"span":[48,60],"store":"smithlab"},"type":"AnchorSelector"},{"exact":"in two steps","prefix":"bond constants for twelve bonds ","suffix":", first to a scan and then to vi","type":"TextQuoteSelector"}],"source":"tag:scient.ing,2026:doc:smithlab/hale2026harmonic-damb","type":"SpecificResource","verdict":"supports"},"created":"2026-10-12T09:00:00Z","creator":{"id":"tag:scient.ing,2026:software:example-model","name":"A model (illustrative)","type":"Software"},"id":"tag:scient.ing,2026:annotation:smithlab/jh0e4st2c0kpt","layer":"tag:scient.ing,2026:layer:smithlab/tr6c3q8m","motivation":"linking","receipt":"qvckzaatz5d1v","status":"confirmed","target":{"selector":[{"anchor":{"doc":"writer2027fixed-ynr7.v3","format":"scienting-anchor/1","kind":"sentence","node":"qgh1k31","node_hash":"93a39490870e694299e88e6f475d42c38078d1442959e555bdbdf58488dcf187","own_hash":"e004436e7b28ff3dc41e0056f231ba707d9be985694e68acb911345a547842ed","quote":{"exact":"two-step fit","prefix":"Its constants come from a ","suffix":" (Hale and Osei, Table 1).1"},"rev":0,"span":[26,38],"store":"smithlab"},"type":"AnchorSelector"},{"exact":"two-step fit","prefix":"Its constants come from a ","suffix":" (Hale and Osei, Table 1).1","type":"TextQuoteSelector"}],"source":"tag:scient.ing,2026:doc:smithlab/writer2027fixed-ynr7.v3","type":"SpecificResource"},"type":"Annotation"},"cited":"fixtures/packages/hale2026harmonic-damb","cited_corrections":[],"citing":"fixtures/packages/writer2027fixed-ynr7.v3","citing_corrections":[],"expect":{"cited":{"node":"mhp0at8","node_hash":"ee42c4780724a411697e3a95daf182d37c792d4689a7172f32929785d5fd968e","result":"current","span":[48,60]},"cites":[],"citing":{"node":"qgh1k31","node_hash":"93a39490870e694299e88e6f475d42c38078d1442959e555bdbdf58488dcf187","result":"current","span":[26,38]},"open":"smithlab/hale2026harmonic-damb#mhp0at8@ee42c4[48:60]","reference":"ebcq5cy","resolution":{"doc":"smithlab/hale2026harmonic-damb","type":"document"},"result":"current"},"id":"current-abstract","op":"resolve","same_as":[]}
|
||||
{"annotation":{"@context":["http://www.w3.org/ns/anno.jsonld","https://paperspec.scient.ing/context/scienting-1.jsonld"],"body":{"kind":"trace","note":"Table 1 lists the fitted constants of both parameter sets.","reference":"ebcq5cy","selector":[{"anchor":{"doc":"hale2026harmonic-damb","format":"scienting-anchor/1","kind":"table","node":"hj4s7pb","node_hash":"6c6fba200faf41cd06fa8b64e06e2aa29ff8848568b859ae42d1902596348643","own_hash":"66310a9ab1c92568f4c6cae012e35d4245d4ca4b89d2542b7c2113a31ad379ed","rev":12,"store":"smithlab"},"type":"AnchorSelector"}],"source":"tag:scient.ing,2026:doc:smithlab/hale2026harmonic-damb","type":"SpecificResource","verdict":"supports"},"created":"2026-10-12T09:10:00Z","creator":{"id":"tag:scient.ing,2026:key:ana","name":"Ana Example","type":"Person"},"id":"tag:scient.ing,2026:annotation:smithlab/fhnynfb8jx0wc","layer":"tag:scient.ing,2026:layer:smithlab/tr6c3q8m","motivation":"linking","receipt":"qvckzaatz5d1v","status":"confirmed","target":{"selector":[{"anchor":{"doc":"writer2027fixed-ynr7.v3","format":"scienting-anchor/1","kind":"sentence","node":"qgh1k31","node_hash":"93a39490870e694299e88e6f475d42c38078d1442959e555bdbdf58488dcf187","own_hash":"e004436e7b28ff3dc41e0056f231ba707d9be985694e68acb911345a547842ed","quote":{"exact":"Its constants come from a two-step fit (Hale and Osei, Table 1).1"},"rev":0,"store":"smithlab"},"type":"AnchorSelector"},{"exact":"Its constants come from a two-step fit (Hale and Osei, Table 1).1","prefix":"","suffix":"","type":"TextQuoteSelector"}],"source":"tag:scient.ing,2026:doc:smithlab/writer2027fixed-ynr7.v3","type":"SpecificResource"},"type":"Annotation"},"cited":"fixtures/packages/hale2026harmonic-damb","cited_corrections":[],"citing":"fixtures/packages/writer2027fixed-ynr7.v3","citing_corrections":[],"expect":{"cited":{"node":"hj4s7pb","node_hash":"6c6fba200faf41cd06fa8b64e06e2aa29ff8848568b859ae42d1902596348643","result":"current"},"cites":[],"citing":{"node":"qgh1k31","node_hash":"93a39490870e694299e88e6f475d42c38078d1442959e555bdbdf58488dcf187","result":"current"},"open":"smithlab/hale2026harmonic-damb#hj4s7pb@6c6fba","reference":"ebcq5cy","resolution":{"doc":"smithlab/hale2026harmonic-damb","type":"document"},"result":"current"},"id":"current-table","op":"resolve","same_as":[]}
|
||||
{"annotation":{"@context":["http://www.w3.org/ns/anno.jsonld","https://paperspec.scient.ing/context/scienting-1.jsonld"],"body":{"kind":"trace","note":"Says harmonic terms are common, not how the constants were fitted.","proposer":{"model":{"name":"example-model","parameters":"998b5161e39e01c8bc43ef81ec89141e15b7ea53b2e936707920a6097dbcb2c5","processor":"local","prompt":"9cc338fba7c62db7266f530cb555a5ee36910d41a0120a3fe7fd7de1944d0931","provider":"Example Models (illustrative)","version":"2026-09","weights":"504b2d340045b92c22682b60020c21103aae7ff41172bb632fbe305eb4af9b5e"},"rank":2,"run":"illustrative-run-2","score":"0.6410"},"reference":"ebcq5cy","selector":[{"anchor":{"doc":"hale2026harmonic-damb","format":"scienting-anchor/1","kind":"sentence","node":"kea5026","node_hash":"b836d5872ca37471fa996be04c538254a8b1e37ca12e8f1b4a40837b503afa33","own_hash":"d673d236c03d9583ecf6e01fa819765ec324dc684bd9bbc6967fa50f216e34d2","quote":{"exact":"classical force fields.1","prefix":"ption of covalent stretching in "},"rev":12,"span":[81,105],"store":"smithlab"},"type":"AnchorSelector"},{"exact":"classical force fields.1","prefix":"ption of covalent stretching in ","suffix":"","type":"TextQuoteSelector"}],"source":"tag:scient.ing,2026:doc:smithlab/hale2026harmonic-damb","type":"SpecificResource"},"created":"2026-10-12T09:00:00Z","creator":{"id":"tag:scient.ing,2026:software:example-model","name":"A model (illustrative)","type":"Software"},"id":"tag:scient.ing,2026:annotation:smithlab/arm3zmj6be9fz","layer":"tag:scient.ing,2026:layer:smithlab/tr6c3q8m","motivation":"linking","status":"rejected","target":{"selector":[{"anchor":{"doc":"writer2027fixed-ynr7.v3","format":"scienting-anchor/1","kind":"sentence","node":"qgh1k31","node_hash":"93a39490870e694299e88e6f475d42c38078d1442959e555bdbdf58488dcf187","own_hash":"e004436e7b28ff3dc41e0056f231ba707d9be985694e68acb911345a547842ed","quote":{"exact":"two-step fit","prefix":"Its constants come from a ","suffix":" (Hale and Osei, Table 1).1"},"rev":0,"span":[26,38],"store":"smithlab"},"type":"AnchorSelector"},{"exact":"two-step fit","prefix":"Its constants come from a ","suffix":" (Hale and Osei, Table 1).1","type":"TextQuoteSelector"}],"source":"tag:scient.ing,2026:doc:smithlab/writer2027fixed-ynr7.v3","type":"SpecificResource"},"type":"Annotation"},"cited":"fixtures/packages/hale2026harmonic-damb","cited_corrections":[],"citing":"fixtures/packages/writer2027fixed-ynr7.v3","citing_corrections":[],"expect":{"cited":{"node":"kea5026","node_hash":"b836d5872ca37471fa996be04c538254a8b1e37ca12e8f1b4a40837b503afa33","result":"current","span":[81,105]},"cites":[{"node":"kea5026","refs":[{"node":"tb6syqq","resolution":{"doi":"10.5555/example.ref1","type":"doi"}}],"run":1,"span":[104,105],"text":"1"}],"citing":{"node":"qgh1k31","node_hash":"93a39490870e694299e88e6f475d42c38078d1442959e555bdbdf58488dcf187","result":"current","span":[26,38]},"open":"smithlab/hale2026harmonic-damb#kea5026@b836d5[81:105]","reference":"ebcq5cy","resolution":{"doc":"smithlab/hale2026harmonic-damb","type":"document"},"result":"current"},"id":"current-candidate-with-citation","op":"resolve","same_as":[]}
|
||||
{"annotation":{"@context":["http://www.w3.org/ns/anno.jsonld","https://paperspec.scient.ing/context/scienting-1.jsonld"],"body":{"kind":"trace","note":"The abstract states the two steps: a scan first, then vibrational frequencies.","proposer":{"model":{"name":"example-model","parameters":"998b5161e39e01c8bc43ef81ec89141e15b7ea53b2e936707920a6097dbcb2c5","processor":"local","prompt":"9cc338fba7c62db7266f530cb555a5ee36910d41a0120a3fe7fd7de1944d0931","provider":"Example Models (illustrative)","version":"2026-09","weights":"504b2d340045b92c22682b60020c21103aae7ff41172bb632fbe305eb4af9b5e"},"rank":1,"run":"illustrative-run-2","score":"0.8731"},"reference":"ebcq5cy","selector":[{"anchor":{"doc":"hale2026harmonic-damb","format":"scienting-anchor/1","kind":"sentence","node":"mhp0at8","node_hash":"ee42c4780724a411697e3a95daf182d37c792d4689a7172f32929785d5fd968e","own_hash":"24cc437784bf358d639755dbf59984ad2c0c7a0e7f9448b23e965039b67f759b","quote":{"exact":"in two steps","prefix":"bond constants for twelve bonds ","suffix":", first to a scan and then to vi"},"rev":12,"span":[48,60],"store":"smithlab"},"type":"AnchorSelector"},{"exact":"in two steps","prefix":"bond constants for twelve bonds ","suffix":", first to a scan and then to vi","type":"TextQuoteSelector"}],"source":"tag:scient.ing,2026:doc:smithlab/hale2026harmonic-damb","type":"SpecificResource","verdict":"supports"},"created":"2026-10-12T09:00:00Z","creator":{"id":"tag:scient.ing,2026:software:example-model","name":"A model (illustrative)","type":"Software"},"id":"tag:scient.ing,2026:annotation:smithlab/jh0e4st2c0kpt","layer":"tag:scient.ing,2026:layer:smithlab/tr6c3q8m","motivation":"linking","receipt":"qvckzaatz5d1v","status":"confirmed","target":{"selector":[{"anchor":{"doc":"writer2027fixed-ynr7.v3","format":"scienting-anchor/1","kind":"sentence","node":"qgh1k31","node_hash":"93a39490870e694299e88e6f475d42c38078d1442959e555bdbdf58488dcf187","own_hash":"e004436e7b28ff3dc41e0056f231ba707d9be985694e68acb911345a547842ed","quote":{"exact":"two-step fit","prefix":"Its constants come from a ","suffix":" (Hale and Osei, Table 1).1"},"rev":0,"span":[26,38],"store":"smithlab"},"type":"AnchorSelector"},{"exact":"two-step fit","prefix":"Its constants come from a ","suffix":" (Hale and Osei, Table 1).1","type":"TextQuoteSelector"}],"source":"tag:scient.ing,2026:doc:smithlab/writer2027fixed-ynr7.v3","type":"SpecificResource"},"type":"Annotation"},"cited":"fixtures/packages/hale2026harmonic-damb","cited_corrections":[{"at":"2026-10-13T12:00:00Z","author":{"key":"ana"},"evidence":[{"by":"ana","kind":"person"}],"find":"twelve bonds","op":"replace","reason":"Vector.","replace":"all bonds","target":"mhp0at8"}],"citing":"fixtures/packages/writer2027fixed-ynr7.v3","citing_corrections":[],"expect":{"cited":{"node":"mhp0at8","node_hash":"555e067abf00f7fe04208dad2f82e7e9a4c41fdf7b375705d80aa6062753506e","result":"reattached","span":[45,57]},"cites":[],"citing":{"node":"qgh1k31","node_hash":"93a39490870e694299e88e6f475d42c38078d1442959e555bdbdf58488dcf187","result":"current","span":[26,38]},"open":"smithlab/hale2026harmonic-damb#mhp0at8@555e06[45:57]","reason":"the cited anchor is reattached","reference":"ebcq5cy","resolution":{"doc":"smithlab/hale2026harmonic-damb","type":"document"},"result":"changed"},"id":"changed-passage-moved","op":"resolve","same_as":[]}
|
||||
{"annotation":{"@context":["http://www.w3.org/ns/anno.jsonld","https://paperspec.scient.ing/context/scienting-1.jsonld"],"body":{"kind":"trace","note":"The abstract states the two steps: a scan first, then vibrational frequencies.","proposer":{"model":{"name":"example-model","parameters":"998b5161e39e01c8bc43ef81ec89141e15b7ea53b2e936707920a6097dbcb2c5","processor":"local","prompt":"9cc338fba7c62db7266f530cb555a5ee36910d41a0120a3fe7fd7de1944d0931","provider":"Example Models (illustrative)","version":"2026-09","weights":"504b2d340045b92c22682b60020c21103aae7ff41172bb632fbe305eb4af9b5e"},"rank":1,"run":"illustrative-run-2","score":"0.8731"},"reference":"ebcq5cy","selector":[{"anchor":{"doc":"hale2026harmonic-damb","format":"scienting-anchor/1","kind":"sentence","node":"mhp0at8","node_hash":"ee42c4780724a411697e3a95daf182d37c792d4689a7172f32929785d5fd968e","own_hash":"24cc437784bf358d639755dbf59984ad2c0c7a0e7f9448b23e965039b67f759b","quote":{"exact":"in two steps","prefix":"bond constants for twelve bonds ","suffix":", first to a scan and then to vi"},"rev":12,"span":[48,60],"store":"smithlab"},"type":"AnchorSelector"},{"exact":"in two steps","prefix":"bond constants for twelve bonds ","suffix":", first to a scan and then to vi","type":"TextQuoteSelector"}],"source":"tag:scient.ing,2026:doc:smithlab/hale2026harmonic-damb","type":"SpecificResource","verdict":"supports"},"created":"2026-10-12T09:00:00Z","creator":{"id":"tag:scient.ing,2026:software:example-model","name":"A model (illustrative)","type":"Software"},"id":"tag:scient.ing,2026:annotation:smithlab/jh0e4st2c0kpt","layer":"tag:scient.ing,2026:layer:smithlab/tr6c3q8m","motivation":"linking","receipt":"qvckzaatz5d1v","status":"confirmed","target":{"selector":[{"anchor":{"doc":"writer2027fixed-ynr7.v3","format":"scienting-anchor/1","kind":"sentence","node":"qgh1k31","node_hash":"93a39490870e694299e88e6f475d42c38078d1442959e555bdbdf58488dcf187","own_hash":"e004436e7b28ff3dc41e0056f231ba707d9be985694e68acb911345a547842ed","quote":{"exact":"two-step fit","prefix":"Its constants come from a ","suffix":" (Hale and Osei, Table 1).1"},"rev":0,"span":[26,38],"store":"smithlab"},"type":"AnchorSelector"},{"exact":"two-step fit","prefix":"Its constants come from a ","suffix":" (Hale and Osei, Table 1).1","type":"TextQuoteSelector"}],"source":"tag:scient.ing,2026:doc:smithlab/writer2027fixed-ynr7.v3","type":"SpecificResource"},"type":"Annotation"},"cited":"fixtures/packages/hale2026harmonic-damb","cited_corrections":[],"citing":"fixtures/packages/writer2027fixed-ynr7.v3","citing_corrections":[{"at":"2026-10-13T12:00:00Z","author":{"key":"ana"},"evidence":[{"by":"ana","kind":"person"}],"find":"Its constants","op":"replace","reason":"Vector.","replace":"The constants","target":"qgh1k31"}],"expect":{"cited":{"node":"mhp0at8","node_hash":"ee42c4780724a411697e3a95daf182d37c792d4689a7172f32929785d5fd968e","result":"current","span":[48,60]},"cites":[],"citing":{"node":"qgh1k31","node_hash":"a43c8dd69593e1300771383c98e27a15120479928d9c77023aef0f7646ec9a1f","result":"reattached","span":[26,38]},"open":"smithlab/hale2026harmonic-damb#mhp0at8@ee42c4[48:60]","reason":"the citing anchor is reattached","reference":"ebcq5cy","resolution":{"doc":"smithlab/hale2026harmonic-damb","type":"document"},"result":"changed"},"id":"changed-sentence","op":"resolve","same_as":[]}
|
||||
{"annotation":{"@context":["http://www.w3.org/ns/anno.jsonld","https://paperspec.scient.ing/context/scienting-1.jsonld"],"body":{"kind":"trace","note":"Table 1 lists the fitted constants of both parameter sets.","reference":"ebcq5cy","selector":[{"anchor":{"doc":"hale2026harmonic-damb","format":"scienting-anchor/1","kind":"table","node":"hj4s7pb","node_hash":"6c6fba200faf41cd06fa8b64e06e2aa29ff8848568b859ae42d1902596348643","own_hash":"66310a9ab1c92568f4c6cae012e35d4245d4ca4b89d2542b7c2113a31ad379ed","rev":12,"store":"smithlab"},"type":"AnchorSelector"}],"source":"tag:scient.ing,2026:doc:smithlab/hale2026harmonic-damb","type":"SpecificResource","verdict":"supports"},"created":"2026-10-12T09:10:00Z","creator":{"id":"tag:scient.ing,2026:key:ana","name":"Ana Example","type":"Person"},"id":"tag:scient.ing,2026:annotation:smithlab/fhnynfb8jx0wc","layer":"tag:scient.ing,2026:layer:smithlab/tr6c3q8m","motivation":"linking","receipt":"qvckzaatz5d1v","status":"confirmed","target":{"selector":[{"anchor":{"doc":"writer2027fixed-ynr7.v3","format":"scienting-anchor/1","kind":"sentence","node":"qgh1k31","node_hash":"93a39490870e694299e88e6f475d42c38078d1442959e555bdbdf58488dcf187","own_hash":"e004436e7b28ff3dc41e0056f231ba707d9be985694e68acb911345a547842ed","quote":{"exact":"Its constants come from a two-step fit (Hale and Osei, Table 1).1"},"rev":0,"store":"smithlab"},"type":"AnchorSelector"},{"exact":"Its constants come from a two-step fit (Hale and Osei, Table 1).1","prefix":"","suffix":"","type":"TextQuoteSelector"}],"source":"tag:scient.ing,2026:doc:smithlab/writer2027fixed-ynr7.v3","type":"SpecificResource"},"type":"Annotation"},"cited":"fixtures/packages/hale2026harmonic-damb","cited_corrections":[{"at":"2026-10-13T12:00:00Z","author":{"key":"ana"},"evidence":[{"kind":"crop","sha256":"de60579bacfab769f614fa74d26d20c7b1aa02caf60af1479dc46b98ddcbc106"}],"find":"553.0","op":"replace","reason":"Vector.","replace":"552.0","target":"jv1741a"}],"citing":"fixtures/packages/writer2027fixed-ynr7.v3","citing_corrections":[],"expect":{"cited":{"drift":{"count":1,"level":"low","share":"0.0400"},"node":"hj4s7pb","node_hash":"269c5d863994ffb4f4f4c796dfa4e1c0dc99d11bdbe77dbb0639c8c7efe1e030","result":"changed-below"},"cites":[],"citing":{"node":"qgh1k31","node_hash":"93a39490870e694299e88e6f475d42c38078d1442959e555bdbdf58488dcf187","result":"current"},"open":"smithlab/hale2026harmonic-damb#hj4s7pb@269c5d","reason":"the cited anchor is changed-below","reference":"ebcq5cy","resolution":{"doc":"smithlab/hale2026harmonic-damb","type":"document"},"result":"changed"},"id":"changed-table","op":"resolve","same_as":[]}
|
||||
{"annotation":{"@context":["http://www.w3.org/ns/anno.jsonld","https://paperspec.scient.ing/context/scienting-1.jsonld"],"body":{"kind":"trace","note":"The abstract states the two steps: a scan first, then vibrational frequencies.","proposer":{"model":{"name":"example-model","parameters":"998b5161e39e01c8bc43ef81ec89141e15b7ea53b2e936707920a6097dbcb2c5","processor":"local","prompt":"9cc338fba7c62db7266f530cb555a5ee36910d41a0120a3fe7fd7de1944d0931","provider":"Example Models (illustrative)","version":"2026-09","weights":"504b2d340045b92c22682b60020c21103aae7ff41172bb632fbe305eb4af9b5e"},"rank":1,"run":"illustrative-run-2","score":"0.8731"},"reference":"ebcq5cy","selector":[{"anchor":{"doc":"hale2026harmonic-damb","format":"scienting-anchor/1","kind":"sentence","node":"mhp0at8","node_hash":"ee42c4780724a411697e3a95daf182d37c792d4689a7172f32929785d5fd968e","own_hash":"24cc437784bf358d639755dbf59984ad2c0c7a0e7f9448b23e965039b67f759b","quote":{"exact":"in two steps","prefix":"bond constants for twelve bonds ","suffix":", first to a scan and then to vi"},"rev":12,"span":[48,60],"store":"smithlab"},"type":"AnchorSelector"},{"exact":"in two steps","prefix":"bond constants for twelve bonds ","suffix":", first to a scan and then to vi","type":"TextQuoteSelector"}],"source":"tag:scient.ing,2026:doc:smithlab/hale2026harmonic-damb","type":"SpecificResource","verdict":"supports"},"created":"2026-10-12T09:00:00Z","creator":{"id":"tag:scient.ing,2026:software:example-model","name":"A model (illustrative)","type":"Software"},"id":"tag:scient.ing,2026:annotation:smithlab/jh0e4st2c0kpt","layer":"tag:scient.ing,2026:layer:smithlab/tr6c3q8m","motivation":"linking","receipt":"qvckzaatz5d1v","status":"confirmed","target":{"selector":[{"anchor":{"doc":"writer2027fixed-ynr7.v3","format":"scienting-anchor/1","kind":"sentence","node":"qgh1k31","node_hash":"93a39490870e694299e88e6f475d42c38078d1442959e555bdbdf58488dcf187","own_hash":"e004436e7b28ff3dc41e0056f231ba707d9be985694e68acb911345a547842ed","quote":{"exact":"two-step fit","prefix":"Its constants come from a ","suffix":" (Hale and Osei, Table 1).1"},"rev":0,"span":[26,38],"store":"smithlab"},"type":"AnchorSelector"},{"exact":"two-step fit","prefix":"Its constants come from a ","suffix":" (Hale and Osei, Table 1).1","type":"TextQuoteSelector"}],"source":"tag:scient.ing,2026:doc:smithlab/writer2027fixed-ynr7.v3","type":"SpecificResource"},"type":"Annotation"},"cited":"fixtures/packages/hale2026harmonic-damb","cited_corrections":[{"at":"2026-10-13T12:00:00Z","author":{"key":"ana"},"evidence":[{"by":"ana","kind":"person"}],"find":"in two steps","op":"replace","reason":"Vector.","replace":"in three stages","target":"mhp0at8"}],"citing":"fixtures/packages/writer2027fixed-ynr7.v3","citing_corrections":[],"expect":{"cited":{"node":"mhp0at8","node_hash":"a4946bd7586cb81977f8b1c85d34de7d15b9a3e5cc33c0308da54b1148e124f9","reason":"the node changed and the quote does not occur exactly once","result":"stale"},"cites":[],"citing":{"node":"qgh1k31","node_hash":"93a39490870e694299e88e6f475d42c38078d1442959e555bdbdf58488dcf187","result":"current","span":[26,38]},"open":null,"reason":"the cited anchor is stale: the node changed and the quote does not occur exactly once","reference":"ebcq5cy","resolution":{"doc":"smithlab/hale2026harmonic-damb","type":"document"},"result":"stale","side":"cited"},"id":"stale-cited","op":"resolve","same_as":[]}
|
||||
{"annotation":{"@context":["http://www.w3.org/ns/anno.jsonld","https://paperspec.scient.ing/context/scienting-1.jsonld"],"body":{"kind":"trace","note":"The abstract states the two steps: a scan first, then vibrational frequencies.","proposer":{"model":{"name":"example-model","parameters":"998b5161e39e01c8bc43ef81ec89141e15b7ea53b2e936707920a6097dbcb2c5","processor":"local","prompt":"9cc338fba7c62db7266f530cb555a5ee36910d41a0120a3fe7fd7de1944d0931","provider":"Example Models (illustrative)","version":"2026-09","weights":"504b2d340045b92c22682b60020c21103aae7ff41172bb632fbe305eb4af9b5e"},"rank":1,"run":"illustrative-run-2","score":"0.8731"},"reference":"ebcq5cy","selector":[{"anchor":{"doc":"hale2026harmonic-damb","format":"scienting-anchor/1","kind":"sentence","node":"mhp0at8","node_hash":"ee42c4780724a411697e3a95daf182d37c792d4689a7172f32929785d5fd968e","own_hash":"24cc437784bf358d639755dbf59984ad2c0c7a0e7f9448b23e965039b67f759b","quote":{"exact":"in two steps","prefix":"bond constants for twelve bonds ","suffix":", first to a scan and then to vi"},"rev":12,"span":[48,60],"store":"smithlab"},"type":"AnchorSelector"},{"exact":"in two steps","prefix":"bond constants for twelve bonds ","suffix":", first to a scan and then to vi","type":"TextQuoteSelector"}],"source":"tag:scient.ing,2026:doc:smithlab/hale2026harmonic-damb","type":"SpecificResource","verdict":"supports"},"created":"2026-10-12T09:00:00Z","creator":{"id":"tag:scient.ing,2026:software:example-model","name":"A model (illustrative)","type":"Software"},"id":"tag:scient.ing,2026:annotation:smithlab/jh0e4st2c0kpt","layer":"tag:scient.ing,2026:layer:smithlab/tr6c3q8m","motivation":"linking","receipt":"qvckzaatz5d1v","status":"confirmed","target":{"selector":[{"anchor":{"doc":"writer2027fixed-ynr7.v3","format":"scienting-anchor/1","kind":"sentence","node":"qgh1k31","node_hash":"93a39490870e694299e88e6f475d42c38078d1442959e555bdbdf58488dcf187","own_hash":"e004436e7b28ff3dc41e0056f231ba707d9be985694e68acb911345a547842ed","quote":{"exact":"two-step fit","prefix":"Its constants come from a ","suffix":" (Hale and Osei, Table 1).1"},"rev":0,"span":[26,38],"store":"smithlab"},"type":"AnchorSelector"},{"exact":"two-step fit","prefix":"Its constants come from a ","suffix":" (Hale and Osei, Table 1).1","type":"TextQuoteSelector"}],"source":"tag:scient.ing,2026:doc:smithlab/writer2027fixed-ynr7.v3","type":"SpecificResource"},"type":"Annotation"},"cited":"fixtures/packages/hale2026harmonic-damb","cited_corrections":[],"citing":"fixtures/packages/writer2027fixed-ynr7.v3","citing_corrections":[{"at":"2026-10-13T12:00:00Z","author":{"key":"ana"},"evidence":[{"by":"ana","kind":"person"}],"find":"two-step","op":"replace","reason":"Vector.","replace":"two-stage","target":"qgh1k31"}],"expect":{"cited":{"node":"mhp0at8","node_hash":"ee42c4780724a411697e3a95daf182d37c792d4689a7172f32929785d5fd968e","result":"current","span":[48,60]},"cites":[],"citing":{"node":"qgh1k31","node_hash":"ad3c22febaf3476b9657fb57668de5c55d12a1df1b569cd9230239d915c1a8a5","reason":"the node changed and the quote does not occur exactly once","result":"stale"},"open":"smithlab/hale2026harmonic-damb#mhp0at8@ee42c4[48:60]","reason":"the citing anchor is stale: the node changed and the quote does not occur exactly once","reference":"ebcq5cy","resolution":{"doc":"smithlab/hale2026harmonic-damb","type":"document"},"result":"stale","side":"citing"},"id":"stale-citing","op":"resolve","same_as":[]}
|
||||
{"annotation":{"@context":["http://www.w3.org/ns/anno.jsonld","https://paperspec.scient.ing/context/scienting-1.jsonld"],"body":{"kind":"trace","note":"The abstract states the two steps: a scan first, then vibrational frequencies.","proposer":{"model":{"name":"example-model","parameters":"998b5161e39e01c8bc43ef81ec89141e15b7ea53b2e936707920a6097dbcb2c5","processor":"local","prompt":"9cc338fba7c62db7266f530cb555a5ee36910d41a0120a3fe7fd7de1944d0931","provider":"Example Models (illustrative)","version":"2026-09","weights":"504b2d340045b92c22682b60020c21103aae7ff41172bb632fbe305eb4af9b5e"},"rank":1,"run":"illustrative-run-2","score":"0.8731"},"reference":"ebcq5cy","selector":[{"anchor":{"doc":"hale2026harmonic-damb","format":"scienting-anchor/1","kind":"sentence","node":"mhp0at8","node_hash":"ee42c4780724a411697e3a95daf182d37c792d4689a7172f32929785d5fd968e","own_hash":"24cc437784bf358d639755dbf59984ad2c0c7a0e7f9448b23e965039b67f759b","quote":{"exact":"in two steps","prefix":"bond constants for twelve bonds ","suffix":", first to a scan and then to vi"},"rev":12,"span":[48,60],"store":"smithlab"},"type":"AnchorSelector"},{"exact":"in two steps","prefix":"bond constants for twelve bonds ","suffix":", first to a scan and then to vi","type":"TextQuoteSelector"}],"source":"tag:scient.ing,2026:doc:smithlab/hale2026harmonic-damb","type":"SpecificResource","verdict":"supports"},"created":"2026-10-12T09:00:00Z","creator":{"id":"tag:scient.ing,2026:software:example-model","name":"A model (illustrative)","type":"Software"},"id":"tag:scient.ing,2026:annotation:smithlab/jh0e4st2c0kpt","layer":"tag:scient.ing,2026:layer:smithlab/tr6c3q8m","motivation":"linking","receipt":"qvckzaatz5d1v","status":"confirmed","target":{"selector":[{"anchor":{"doc":"writer2027fixed-ynr7.v3","format":"scienting-anchor/1","kind":"sentence","node":"qgh1k31","node_hash":"93a39490870e694299e88e6f475d42c38078d1442959e555bdbdf58488dcf187","own_hash":"e004436e7b28ff3dc41e0056f231ba707d9be985694e68acb911345a547842ed","quote":{"exact":"two-step fit","prefix":"Its constants come from a ","suffix":" (Hale and Osei, Table 1).1"},"rev":0,"span":[26,38],"store":"smithlab"},"type":"AnchorSelector"},{"exact":"two-step fit","prefix":"Its constants come from a ","suffix":" (Hale and Osei, Table 1).1","type":"TextQuoteSelector"}],"source":"tag:scient.ing,2026:doc:smithlab/writer2027fixed-ynr7.v3","type":"SpecificResource"},"type":"Annotation"},"cited":"fixtures/packages/hale2026harmonic-damb","cited_corrections":[{"at":"2026-10-13T12:00:00Z","author":{"key":"ana"},"evidence":[{"by":"ana","kind":"person"}],"find":"in two steps","op":"replace","reason":"Vector.","replace":"in three stages","target":"mhp0at8"}],"citing":"fixtures/packages/writer2027fixed-ynr7.v3","citing_corrections":[{"at":"2026-10-13T12:00:00Z","author":{"key":"ana"},"evidence":[{"by":"ana","kind":"person"}],"find":"two-step","op":"replace","reason":"Vector.","replace":"two-stage","target":"qgh1k31"}],"expect":{"cited":{"node":"mhp0at8","node_hash":"a4946bd7586cb81977f8b1c85d34de7d15b9a3e5cc33c0308da54b1148e124f9","reason":"the node changed and the quote does not occur exactly once","result":"stale"},"cites":[],"citing":{"node":"qgh1k31","node_hash":"ad3c22febaf3476b9657fb57668de5c55d12a1df1b569cd9230239d915c1a8a5","reason":"the node changed and the quote does not occur exactly once","result":"stale"},"open":null,"reason":"the citing anchor is stale: the node changed and the quote does not occur exactly once; the cited anchor is stale: the node changed and the quote does not occur exactly once","reference":"ebcq5cy","resolution":{"doc":"smithlab/hale2026harmonic-damb","type":"document"},"result":"stale","side":"both"},"id":"stale-both","op":"resolve","same_as":[]}
|
||||
{"annotation":{"@context":["http://www.w3.org/ns/anno.jsonld","https://paperspec.scient.ing/context/scienting-1.jsonld"],"body":{"kind":"trace","note":"Table 1 lists the fitted constants of both parameter sets.","reference":"ebcq5cy","selector":[{"anchor":{"doc":"hale2026harmonic-damb","format":"scienting-anchor/1","kind":"table","node":"hj4s7pb","node_hash":"6c6fba200faf41cd06fa8b64e06e2aa29ff8848568b859ae42d1902596348643","own_hash":"66310a9ab1c92568f4c6cae012e35d4245d4ca4b89d2542b7c2113a31ad379ed","rev":12,"store":"smithlab"},"type":"AnchorSelector"}],"source":"tag:scient.ing,2026:doc:smithlab/hale2026harmonic-damb","type":"SpecificResource","verdict":"supports"},"created":"2026-10-12T09:10:00Z","creator":{"id":"tag:scient.ing,2026:key:ana","name":"Ana Example","type":"Person"},"id":"tag:scient.ing,2026:annotation:smithlab/fhnynfb8jx0wc","layer":"tag:scient.ing,2026:layer:smithlab/tr6c3q8m","motivation":"linking","receipt":"qvckzaatz5d1v","status":"confirmed","target":{"selector":[{"anchor":{"doc":"writer2027fixed-ynr7.v3","format":"scienting-anchor/1","kind":"sentence","node":"qgh1k31","node_hash":"93a39490870e694299e88e6f475d42c38078d1442959e555bdbdf58488dcf187","own_hash":"e004436e7b28ff3dc41e0056f231ba707d9be985694e68acb911345a547842ed","quote":{"exact":"Its constants come from a two-step fit (Hale and Osei, Table 1).1"},"rev":0,"store":"smithlab"},"type":"AnchorSelector"},{"exact":"Its constants come from a two-step fit (Hale and Osei, Table 1).1","prefix":"","suffix":"","type":"TextQuoteSelector"}],"source":"tag:scient.ing,2026:doc:smithlab/writer2027fixed-ynr7.v3","type":"SpecificResource"},"type":"Annotation"},"cited":"fixtures/packages/hale2026harmonic-damb","cited_corrections":[],"citing":"fixtures/packages/writer2027fixed-ynr7.v3","citing_corrections":[{"at":"2026-10-13T12:00:00Z","author":{"key":"ana"},"evidence":[{"by":"ana","kind":"person"}],"find":"two-step","op":"replace","reason":"Vector.","replace":"two-stage","target":"qgh1k31"}],"expect":{"cited":{"node":"hj4s7pb","node_hash":"6c6fba200faf41cd06fa8b64e06e2aa29ff8848568b859ae42d1902596348643","result":"current"},"cites":[],"citing":{"node":"qgh1k31","node_hash":"ad3c22febaf3476b9657fb57668de5c55d12a1df1b569cd9230239d915c1a8a5","reason":"the node changed and the quote does not occur exactly once","result":"stale"},"open":"smithlab/hale2026harmonic-damb#hj4s7pb@6c6fba","reason":"the citing anchor is stale: the node changed and the quote does not occur exactly once","reference":"ebcq5cy","resolution":{"doc":"smithlab/hale2026harmonic-damb","type":"document"},"result":"stale","side":"citing"},"id":"stale-citing-whole-sentence","op":"resolve","same_as":[]}
|
||||
{"annotation":{"@context":["http://www.w3.org/ns/anno.jsonld","https://paperspec.scient.ing/context/scienting-1.jsonld"],"body":{"kind":"trace","note":"The abstract states the two steps: a scan first, then vibrational frequencies.","proposer":{"model":{"name":"example-model","parameters":"998b5161e39e01c8bc43ef81ec89141e15b7ea53b2e936707920a6097dbcb2c5","processor":"local","prompt":"9cc338fba7c62db7266f530cb555a5ee36910d41a0120a3fe7fd7de1944d0931","provider":"Example Models (illustrative)","version":"2026-09","weights":"504b2d340045b92c22682b60020c21103aae7ff41172bb632fbe305eb4af9b5e"},"rank":1,"run":"illustrative-run-2","score":"0.8731"},"reference":"ebcq5cy","selector":[{"anchor":{"doc":"hale2026harmonic-damb","format":"scienting-anchor/1","kind":"sentence","node":"mhp0at8","node_hash":"ee42c4780724a411697e3a95daf182d37c792d4689a7172f32929785d5fd968e","own_hash":"24cc437784bf358d639755dbf59984ad2c0c7a0e7f9448b23e965039b67f759b","quote":{"exact":"in two steps","prefix":"bond constants for twelve bonds ","suffix":", first to a scan and then to vi"},"rev":12,"span":[48,60],"store":"smithlab"},"type":"AnchorSelector"},{"exact":"in two steps","prefix":"bond constants for twelve bonds ","suffix":", first to a scan and then to vi","type":"TextQuoteSelector"}],"source":"tag:scient.ing,2026:doc:smithlab/hale2026harmonic-damb","type":"SpecificResource","verdict":"supports"},"created":"2026-10-12T09:00:00Z","creator":{"id":"tag:scient.ing,2026:software:example-model","name":"A model (illustrative)","type":"Software"},"id":"tag:scient.ing,2026:annotation:smithlab/jh0e4st2c0kpt","layer":"tag:scient.ing,2026:layer:smithlab/tr6c3q8m","motivation":"linking","receipt":"qvckzaatz5d1v","status":"confirmed","target":{"selector":[{"anchor":{"doc":"writer2027fixed-ynr7.v3","format":"scienting-anchor/1","kind":"sentence","node":"qgh1k31","node_hash":"93a39490870e694299e88e6f475d42c38078d1442959e555bdbdf58488dcf187","own_hash":"e004436e7b28ff3dc41e0056f231ba707d9be985694e68acb911345a547842ed","quote":{"exact":"two-step fit","prefix":"Its constants come from a ","suffix":" (Hale and Osei, Table 1).1"},"rev":0,"span":[26,38],"store":"smithlab"},"type":"AnchorSelector"},{"exact":"two-step fit","prefix":"Its constants come from a ","suffix":" (Hale and Osei, Table 1).1","type":"TextQuoteSelector"}],"source":"tag:scient.ing,2026:doc:smithlab/writer2027fixed-ynr7.v3","type":"SpecificResource"},"type":"Annotation"},"cited":"fixtures/packages/hale2026harmonic-damb","cited_corrections":[],"citing":"fixtures/packages/writer2027fixed-ynr7.v3","citing_corrections":[{"at":"2026-10-13T12:00:00Z","author":{"key":"ana"},"evidence":[{"by":"ana","kind":"person"}],"op":"relink","reason":"Vector.","refs":null,"run":3,"target":"qgh1k31"}],"expect":{"cited":{"node":"mhp0at8","node_hash":"ee42c4780724a411697e3a95daf182d37c792d4689a7172f32929785d5fd968e","result":"current","span":[48,60]},"cites":[],"citing":{"node":"qgh1k31","node_hash":"401c7460dc29e50f3e6ecd8968118b41d7cfd8be24c81e6ef388eb66c7361044","result":"reattached","span":[26,38]},"open":"smithlab/hale2026harmonic-damb#mhp0at8@ee42c4[48:60]","reason":"the sentence no longer cites the reference","reference":"ebcq5cy","resolution":{"doc":"smithlab/hale2026harmonic-damb","type":"document"},"result":"unlinked"},"id":"unlinked","op":"resolve","same_as":[]}
|
||||
{"annotation":{"@context":["http://www.w3.org/ns/anno.jsonld","https://paperspec.scient.ing/context/scienting-1.jsonld"],"body":{"kind":"trace","note":"The abstract states the two steps: a scan first, then vibrational frequencies.","proposer":{"model":{"name":"example-model","parameters":"998b5161e39e01c8bc43ef81ec89141e15b7ea53b2e936707920a6097dbcb2c5","processor":"local","prompt":"9cc338fba7c62db7266f530cb555a5ee36910d41a0120a3fe7fd7de1944d0931","provider":"Example Models (illustrative)","version":"2026-09","weights":"504b2d340045b92c22682b60020c21103aae7ff41172bb632fbe305eb4af9b5e"},"rank":1,"run":"illustrative-run-2","score":"0.8731"},"reference":"ebcq5cy","selector":[{"anchor":{"doc":"hale2026harmonic-damb","format":"scienting-anchor/1","kind":"sentence","node":"mhp0at8","node_hash":"ee42c4780724a411697e3a95daf182d37c792d4689a7172f32929785d5fd968e","own_hash":"24cc437784bf358d639755dbf59984ad2c0c7a0e7f9448b23e965039b67f759b","quote":{"exact":"in two steps","prefix":"bond constants for twelve bonds ","suffix":", first to a scan and then to vi"},"rev":12,"span":[48,60],"store":"smithlab"},"type":"AnchorSelector"},{"exact":"in two steps","prefix":"bond constants for twelve bonds ","suffix":", first to a scan and then to vi","type":"TextQuoteSelector"}],"source":"tag:scient.ing,2026:doc:smithlab/hale2026harmonic-damb","type":"SpecificResource","verdict":"supports"},"created":"2026-10-12T09:00:00Z","creator":{"id":"tag:scient.ing,2026:software:example-model","name":"A model (illustrative)","type":"Software"},"id":"tag:scient.ing,2026:annotation:smithlab/jh0e4st2c0kpt","layer":"tag:scient.ing,2026:layer:smithlab/tr6c3q8m","motivation":"linking","receipt":"qvckzaatz5d1v","status":"confirmed","target":{"selector":[{"anchor":{"doc":"writer2027fixed-ynr7.v3","format":"scienting-anchor/1","kind":"sentence","node":"qgh1k31","node_hash":"93a39490870e694299e88e6f475d42c38078d1442959e555bdbdf58488dcf187","own_hash":"e004436e7b28ff3dc41e0056f231ba707d9be985694e68acb911345a547842ed","quote":{"exact":"two-step fit","prefix":"Its constants come from a ","suffix":" (Hale and Osei, Table 1).1"},"rev":0,"span":[26,38],"store":"smithlab"},"type":"AnchorSelector"},{"exact":"two-step fit","prefix":"Its constants come from a ","suffix":" (Hale and Osei, Table 1).1","type":"TextQuoteSelector"}],"source":"tag:scient.ing,2026:doc:smithlab/writer2027fixed-ynr7.v3","type":"SpecificResource"},"type":"Annotation"},"cited":"fixtures/packages/hale2026harmonic-damb","cited_corrections":[],"citing":"fixtures/packages/writer2027fixed-ynr7.v3","citing_corrections":[{"at":"2026-10-13T12:00:00Z","author":{"key":"ana"},"evidence":[{"by":"ana","kind":"person"}],"op":"resolve","reason":"Vector.","resolution":{"doi":"10.5555/example.other.2020","type":"doi"},"target":"ebcq5cy"}],"expect":{"cited":{"node":"mhp0at8","node_hash":"ee42c4780724a411697e3a95daf182d37c792d4689a7172f32929785d5fd968e","result":"current","span":[48,60]},"cites":[],"citing":{"node":"qgh1k31","node_hash":"93a39490870e694299e88e6f475d42c38078d1442959e555bdbdf58488dcf187","result":"current","span":[26,38]},"open":"smithlab/hale2026harmonic-damb#mhp0at8@ee42c4[48:60]","reason":"the reference resolves to doi:10.5555/example.other.2020, not to the cited work","reference":"ebcq5cy","resolution":{"doi":"10.5555/example.other.2020","type":"doi"},"result":"misresolved"},"id":"misresolved-doi","op":"resolve","same_as":["doi:10.5555/example.hale.2026"]}
|
||||
{"annotation":{"@context":["http://www.w3.org/ns/anno.jsonld","https://paperspec.scient.ing/context/scienting-1.jsonld"],"body":{"kind":"trace","note":"The abstract states the two steps: a scan first, then vibrational frequencies.","proposer":{"model":{"name":"example-model","parameters":"998b5161e39e01c8bc43ef81ec89141e15b7ea53b2e936707920a6097dbcb2c5","processor":"local","prompt":"9cc338fba7c62db7266f530cb555a5ee36910d41a0120a3fe7fd7de1944d0931","provider":"Example Models (illustrative)","version":"2026-09","weights":"504b2d340045b92c22682b60020c21103aae7ff41172bb632fbe305eb4af9b5e"},"rank":1,"run":"illustrative-run-2","score":"0.8731"},"reference":"ebcq5cy","selector":[{"anchor":{"doc":"hale2026harmonic-damb","format":"scienting-anchor/1","kind":"sentence","node":"mhp0at8","node_hash":"ee42c4780724a411697e3a95daf182d37c792d4689a7172f32929785d5fd968e","own_hash":"24cc437784bf358d639755dbf59984ad2c0c7a0e7f9448b23e965039b67f759b","quote":{"exact":"in two steps","prefix":"bond constants for twelve bonds ","suffix":", first to a scan and then to vi"},"rev":12,"span":[48,60],"store":"smithlab"},"type":"AnchorSelector"},{"exact":"in two steps","prefix":"bond constants for twelve bonds ","suffix":", first to a scan and then to vi","type":"TextQuoteSelector"}],"source":"tag:scient.ing,2026:doc:smithlab/hale2026harmonic-damb","type":"SpecificResource","verdict":"supports"},"created":"2026-10-12T09:00:00Z","creator":{"id":"tag:scient.ing,2026:software:example-model","name":"A model (illustrative)","type":"Software"},"id":"tag:scient.ing,2026:annotation:smithlab/jh0e4st2c0kpt","layer":"tag:scient.ing,2026:layer:smithlab/tr6c3q8m","motivation":"linking","receipt":"qvckzaatz5d1v","status":"confirmed","target":{"selector":[{"anchor":{"doc":"writer2027fixed-ynr7.v3","format":"scienting-anchor/1","kind":"sentence","node":"qgh1k31","node_hash":"93a39490870e694299e88e6f475d42c38078d1442959e555bdbdf58488dcf187","own_hash":"e004436e7b28ff3dc41e0056f231ba707d9be985694e68acb911345a547842ed","quote":{"exact":"two-step fit","prefix":"Its constants come from a ","suffix":" (Hale and Osei, Table 1).1"},"rev":0,"span":[26,38],"store":"smithlab"},"type":"AnchorSelector"},{"exact":"two-step fit","prefix":"Its constants come from a ","suffix":" (Hale and Osei, Table 1).1","type":"TextQuoteSelector"}],"source":"tag:scient.ing,2026:doc:smithlab/writer2027fixed-ynr7.v3","type":"SpecificResource"},"type":"Annotation"},"cited":"fixtures/packages/hale2026harmonic-damb","cited_corrections":[],"citing":"fixtures/packages/writer2027fixed-ynr7.v3","citing_corrections":[{"at":"2026-10-13T12:00:00Z","author":{"key":"ana"},"evidence":[{"by":"ana","kind":"person"}],"op":"resolve","reason":"Vector.","resolution":{"doc":"smithlab/okafor2025solvent-acth","type":"document"},"target":"ebcq5cy"}],"expect":{"cited":{"node":"mhp0at8","node_hash":"ee42c4780724a411697e3a95daf182d37c792d4689a7172f32929785d5fd968e","result":"current","span":[48,60]},"cites":[],"citing":{"node":"qgh1k31","node_hash":"93a39490870e694299e88e6f475d42c38078d1442959e555bdbdf58488dcf187","result":"current","span":[26,38]},"open":"smithlab/hale2026harmonic-damb#mhp0at8@ee42c4[48:60]","reason":"the reference resolves to smithlab/okafor2025solvent-acth, not to the cited work","reference":"ebcq5cy","resolution":{"doc":"smithlab/okafor2025solvent-acth","type":"document"},"result":"misresolved"},"id":"misresolved-document","op":"resolve","same_as":["doi:10.5555/example.hale.2026"]}
|
||||
{"annotation":{"@context":["http://www.w3.org/ns/anno.jsonld","https://paperspec.scient.ing/context/scienting-1.jsonld"],"body":{"kind":"trace","note":"The abstract states the two steps: a scan first, then vibrational frequencies.","proposer":{"model":{"name":"example-model","parameters":"998b5161e39e01c8bc43ef81ec89141e15b7ea53b2e936707920a6097dbcb2c5","processor":"local","prompt":"9cc338fba7c62db7266f530cb555a5ee36910d41a0120a3fe7fd7de1944d0931","provider":"Example Models (illustrative)","version":"2026-09","weights":"504b2d340045b92c22682b60020c21103aae7ff41172bb632fbe305eb4af9b5e"},"rank":1,"run":"illustrative-run-2","score":"0.8731"},"reference":"ebcq5cy","selector":[{"anchor":{"doc":"hale2026harmonic-damb","format":"scienting-anchor/1","kind":"sentence","node":"mhp0at8","node_hash":"ee42c4780724a411697e3a95daf182d37c792d4689a7172f32929785d5fd968e","own_hash":"24cc437784bf358d639755dbf59984ad2c0c7a0e7f9448b23e965039b67f759b","quote":{"exact":"in two steps","prefix":"bond constants for twelve bonds ","suffix":", first to a scan and then to vi"},"rev":12,"span":[48,60],"store":"smithlab"},"type":"AnchorSelector"},{"exact":"in two steps","prefix":"bond constants for twelve bonds ","suffix":", first to a scan and then to vi","type":"TextQuoteSelector"}],"source":"tag:scient.ing,2026:doc:smithlab/hale2026harmonic-damb","type":"SpecificResource","verdict":"supports"},"created":"2026-10-12T09:00:00Z","creator":{"id":"tag:scient.ing,2026:software:example-model","name":"A model (illustrative)","type":"Software"},"id":"tag:scient.ing,2026:annotation:smithlab/jh0e4st2c0kpt","layer":"tag:scient.ing,2026:layer:smithlab/tr6c3q8m","motivation":"linking","receipt":"qvckzaatz5d1v","status":"confirmed","target":{"selector":[{"anchor":{"doc":"writer2027fixed-ynr7.v3","format":"scienting-anchor/1","kind":"sentence","node":"qgh1k31","node_hash":"93a39490870e694299e88e6f475d42c38078d1442959e555bdbdf58488dcf187","own_hash":"e004436e7b28ff3dc41e0056f231ba707d9be985694e68acb911345a547842ed","quote":{"exact":"two-step fit","prefix":"Its constants come from a ","suffix":" (Hale and Osei, Table 1).1"},"rev":0,"span":[26,38],"store":"smithlab"},"type":"AnchorSelector"},{"exact":"two-step fit","prefix":"Its constants come from a ","suffix":" (Hale and Osei, Table 1).1","type":"TextQuoteSelector"}],"source":"tag:scient.ing,2026:doc:smithlab/writer2027fixed-ynr7.v3","type":"SpecificResource"},"type":"Annotation"},"cited":"fixtures/packages/hale2026harmonic-damb","cited_corrections":[],"citing":"fixtures/packages/writer2027fixed-ynr7.v3","citing_corrections":[{"at":"2026-10-13T12:00:00Z","author":{"key":"ana"},"evidence":[{"by":"ana","kind":"person"}],"op":"resolve","reason":"Vector.","resolution":{"doi":"10.5555/example.hale.2026","type":"doi"},"target":"ebcq5cy"}],"expect":{"cited":{"node":"mhp0at8","node_hash":"ee42c4780724a411697e3a95daf182d37c792d4689a7172f32929785d5fd968e","result":"current","span":[48,60]},"cites":[],"citing":{"node":"qgh1k31","node_hash":"93a39490870e694299e88e6f475d42c38078d1442959e555bdbdf58488dcf187","result":"current","span":[26,38]},"open":"smithlab/hale2026harmonic-damb#mhp0at8@ee42c4[48:60]","reason":"the reference resolves to doi:10.5555/example.hale.2026, not to the cited work","reference":"ebcq5cy","resolution":{"doi":"10.5555/example.hale.2026","type":"doi"},"result":"misresolved"},"id":"misresolved-doi-without-same-as","op":"resolve","same_as":[]}
|
||||
{"annotation":{"@context":["http://www.w3.org/ns/anno.jsonld","https://paperspec.scient.ing/context/scienting-1.jsonld"],"body":{"kind":"trace","note":"The abstract states the two steps: a scan first, then vibrational frequencies.","proposer":{"model":{"name":"example-model","parameters":"998b5161e39e01c8bc43ef81ec89141e15b7ea53b2e936707920a6097dbcb2c5","processor":"local","prompt":"9cc338fba7c62db7266f530cb555a5ee36910d41a0120a3fe7fd7de1944d0931","provider":"Example Models (illustrative)","version":"2026-09","weights":"504b2d340045b92c22682b60020c21103aae7ff41172bb632fbe305eb4af9b5e"},"rank":1,"run":"illustrative-run-2","score":"0.8731"},"reference":"ebcq5cy","selector":[{"anchor":{"doc":"hale2026harmonic-damb","format":"scienting-anchor/1","kind":"sentence","node":"mhp0at8","node_hash":"ee42c4780724a411697e3a95daf182d37c792d4689a7172f32929785d5fd968e","own_hash":"24cc437784bf358d639755dbf59984ad2c0c7a0e7f9448b23e965039b67f759b","quote":{"exact":"in two steps","prefix":"bond constants for twelve bonds ","suffix":", first to a scan and then to vi"},"rev":12,"span":[48,60],"store":"smithlab"},"type":"AnchorSelector"},{"exact":"in two steps","prefix":"bond constants for twelve bonds ","suffix":", first to a scan and then to vi","type":"TextQuoteSelector"}],"source":"tag:scient.ing,2026:doc:smithlab/hale2026harmonic-damb","type":"SpecificResource","verdict":"supports"},"created":"2026-10-12T09:00:00Z","creator":{"id":"tag:scient.ing,2026:software:example-model","name":"A model (illustrative)","type":"Software"},"id":"tag:scient.ing,2026:annotation:smithlab/jh0e4st2c0kpt","layer":"tag:scient.ing,2026:layer:smithlab/tr6c3q8m","motivation":"linking","receipt":"qvckzaatz5d1v","status":"confirmed","target":{"selector":[{"anchor":{"doc":"writer2027fixed-ynr7.v3","format":"scienting-anchor/1","kind":"sentence","node":"qgh1k31","node_hash":"93a39490870e694299e88e6f475d42c38078d1442959e555bdbdf58488dcf187","own_hash":"e004436e7b28ff3dc41e0056f231ba707d9be985694e68acb911345a547842ed","quote":{"exact":"two-step fit","prefix":"Its constants come from a ","suffix":" (Hale and Osei, Table 1).1"},"rev":0,"span":[26,38],"store":"smithlab"},"type":"AnchorSelector"},{"exact":"two-step fit","prefix":"Its constants come from a ","suffix":" (Hale and Osei, Table 1).1","type":"TextQuoteSelector"}],"source":"tag:scient.ing,2026:doc:smithlab/writer2027fixed-ynr7.v3","type":"SpecificResource"},"type":"Annotation"},"cited":"fixtures/packages/hale2026harmonic-damb","cited_corrections":[],"citing":"fixtures/packages/writer2027fixed-ynr7.v3","citing_corrections":[{"at":"2026-10-13T12:00:00Z","author":{"key":"ana"},"evidence":[{"by":"ana","kind":"person"}],"op":"resolve","reason":"Vector.","resolution":{"doi":"10.5555/Example.Hale.2026","type":"doi"},"target":"ebcq5cy"}],"expect":{"cited":{"node":"mhp0at8","node_hash":"ee42c4780724a411697e3a95daf182d37c792d4689a7172f32929785d5fd968e","result":"current","span":[48,60]},"cites":[],"citing":{"node":"qgh1k31","node_hash":"93a39490870e694299e88e6f475d42c38078d1442959e555bdbdf58488dcf187","result":"current","span":[26,38]},"open":"smithlab/hale2026harmonic-damb#mhp0at8@ee42c4[48:60]","reference":"ebcq5cy","resolution":{"doi":"10.5555/Example.Hale.2026","type":"doi"},"result":"current"},"id":"current-doi-in-same-as","op":"resolve","same_as":["doi:10.5555/example.hale.2026"]}
|
||||
{"annotation":{"@context":["http://www.w3.org/ns/anno.jsonld","https://paperspec.scient.ing/context/scienting-1.jsonld"],"body":{"kind":"trace","note":"The abstract states the two steps: a scan first, then vibrational frequencies.","proposer":{"model":{"name":"example-model","parameters":"998b5161e39e01c8bc43ef81ec89141e15b7ea53b2e936707920a6097dbcb2c5","processor":"local","prompt":"9cc338fba7c62db7266f530cb555a5ee36910d41a0120a3fe7fd7de1944d0931","provider":"Example Models (illustrative)","version":"2026-09","weights":"504b2d340045b92c22682b60020c21103aae7ff41172bb632fbe305eb4af9b5e"},"rank":1,"run":"illustrative-run-2","score":"0.8731"},"reference":"ebcq5cy","selector":[{"anchor":{"doc":"hale2026harmonic-damb","format":"scienting-anchor/1","kind":"sentence","node":"mhp0at8","node_hash":"ee42c4780724a411697e3a95daf182d37c792d4689a7172f32929785d5fd968e","own_hash":"24cc437784bf358d639755dbf59984ad2c0c7a0e7f9448b23e965039b67f759b","quote":{"exact":"in two steps","prefix":"bond constants for twelve bonds ","suffix":", first to a scan and then to vi"},"rev":12,"span":[48,60],"store":"smithlab"},"type":"AnchorSelector"},{"exact":"in two steps","prefix":"bond constants for twelve bonds ","suffix":", first to a scan and then to vi","type":"TextQuoteSelector"}],"source":"tag:scient.ing,2026:doc:smithlab/hale2026harmonic-damb","type":"SpecificResource","verdict":"supports"},"created":"2026-10-12T09:00:00Z","creator":{"id":"tag:scient.ing,2026:software:example-model","name":"A model (illustrative)","type":"Software"},"id":"tag:scient.ing,2026:annotation:smithlab/jh0e4st2c0kpt","layer":"tag:scient.ing,2026:layer:smithlab/tr6c3q8m","motivation":"linking","receipt":"qvckzaatz5d1v","status":"confirmed","target":{"selector":[{"anchor":{"doc":"writer2027fixed-ynr7.v3","format":"scienting-anchor/1","kind":"sentence","node":"qgh1k31","node_hash":"93a39490870e694299e88e6f475d42c38078d1442959e555bdbdf58488dcf187","own_hash":"e004436e7b28ff3dc41e0056f231ba707d9be985694e68acb911345a547842ed","quote":{"exact":"two-step fit","prefix":"Its constants come from a ","suffix":" (Hale and Osei, Table 1).1"},"rev":0,"span":[26,38],"store":"smithlab"},"type":"AnchorSelector"},{"exact":"two-step fit","prefix":"Its constants come from a ","suffix":" (Hale and Osei, Table 1).1","type":"TextQuoteSelector"}],"source":"tag:scient.ing,2026:doc:smithlab/writer2027fixed-ynr7.v3","type":"SpecificResource"},"type":"Annotation"},"cited":"fixtures/packages/hale2026harmonic-damb","cited_corrections":[],"citing":"fixtures/packages/writer2027fixed-ynr7.v3","citing_corrections":[{"at":"2026-10-13T12:00:00Z","author":{"key":"ana"},"evidence":[{"by":"ana","kind":"person"}],"op":"resolve","reason":"Vector.","resolution":{"doc":"smithlab/hale2026harmonic-damb.v2","type":"document"},"target":"ebcq5cy"}],"expect":{"cited":{"node":"mhp0at8","node_hash":"ee42c4780724a411697e3a95daf182d37c792d4689a7172f32929785d5fd968e","result":"current","span":[48,60]},"cites":[],"citing":{"node":"qgh1k31","node_hash":"93a39490870e694299e88e6f475d42c38078d1442959e555bdbdf58488dcf187","result":"current","span":[26,38]},"open":"smithlab/hale2026harmonic-damb#mhp0at8@ee42c4[48:60]","reference":"ebcq5cy","resolution":{"doc":"smithlab/hale2026harmonic-damb.v2","type":"document"},"result":"current"},"id":"current-another-version","op":"resolve","same_as":[]}
|
||||
{"annotation":{"@context":["http://www.w3.org/ns/anno.jsonld","https://paperspec.scient.ing/context/scienting-1.jsonld"],"body":{"kind":"trace","note":"The abstract states the two steps: a scan first, then vibrational frequencies.","proposer":{"model":{"name":"example-model","parameters":"998b5161e39e01c8bc43ef81ec89141e15b7ea53b2e936707920a6097dbcb2c5","processor":"local","prompt":"9cc338fba7c62db7266f530cb555a5ee36910d41a0120a3fe7fd7de1944d0931","provider":"Example Models (illustrative)","version":"2026-09","weights":"504b2d340045b92c22682b60020c21103aae7ff41172bb632fbe305eb4af9b5e"},"rank":1,"run":"illustrative-run-2","score":"0.8731"},"reference":"ebcq5cy","selector":[{"anchor":{"doc":"hale2026harmonic-damb","format":"scienting-anchor/1","kind":"sentence","node":"mhp0at8","node_hash":"ee42c4780724a411697e3a95daf182d37c792d4689a7172f32929785d5fd968e","own_hash":"24cc437784bf358d639755dbf59984ad2c0c7a0e7f9448b23e965039b67f759b","quote":{"exact":"in two steps","prefix":"bond constants for twelve bonds ","suffix":", first to a scan and then to vi"},"rev":12,"span":[48,60],"store":"smithlab"},"type":"AnchorSelector"},{"exact":"in two steps","prefix":"bond constants for twelve bonds ","suffix":", first to a scan and then to vi","type":"TextQuoteSelector"}],"source":"tag:scient.ing,2026:doc:smithlab/hale2026harmonic-damb","type":"SpecificResource","verdict":"supports"},"created":"2026-10-12T09:00:00Z","creator":{"id":"tag:scient.ing,2026:software:example-model","name":"A model (illustrative)","type":"Software"},"id":"tag:scient.ing,2026:annotation:smithlab/jh0e4st2c0kpt","layer":"tag:scient.ing,2026:layer:smithlab/tr6c3q8m","motivation":"linking","receipt":"qvckzaatz5d1v","status":"confirmed","target":{"selector":[{"anchor":{"doc":"writer2027fixed-ynr7.v3","format":"scienting-anchor/1","kind":"sentence","node":"qgh1k31","node_hash":"93a39490870e694299e88e6f475d42c38078d1442959e555bdbdf58488dcf187","own_hash":"e004436e7b28ff3dc41e0056f231ba707d9be985694e68acb911345a547842ed","quote":{"exact":"two-step fit","prefix":"Its constants come from a ","suffix":" (Hale and Osei, Table 1).1"},"rev":0,"span":[26,38],"store":"smithlab"},"type":"AnchorSelector"},{"exact":"two-step fit","prefix":"Its constants come from a ","suffix":" (Hale and Osei, Table 1).1","type":"TextQuoteSelector"}],"source":"tag:scient.ing,2026:doc:smithlab/writer2027fixed-ynr7.v3","type":"SpecificResource"},"type":"Annotation"},"cited":"fixtures/packages/hale2026harmonic-damb","cited_corrections":[],"citing":"fixtures/packages/writer2027fixed-ynr7.v3","citing_corrections":[{"at":"2026-10-13T12:00:00Z","author":{"key":"ana"},"evidence":[{"by":"ana","kind":"person"}],"op":"resolve","reason":"Vector.","resolution":{"type":"none"},"target":"ebcq5cy"}],"expect":{"cited":{"node":"mhp0at8","node_hash":"ee42c4780724a411697e3a95daf182d37c792d4689a7172f32929785d5fd968e","result":"current","span":[48,60]},"cites":[],"citing":{"node":"qgh1k31","node_hash":"93a39490870e694299e88e6f475d42c38078d1442959e555bdbdf58488dcf187","result":"current","span":[26,38]},"open":"smithlab/hale2026harmonic-damb#mhp0at8@ee42c4[48:60]","reference":"ebcq5cy","resolution":{"type":"none"},"result":"current"},"id":"current-reference-unresolved","op":"resolve","same_as":[]}
|
||||
{"annotation":{"@context":["http://www.w3.org/ns/anno.jsonld","https://paperspec.scient.ing/context/scienting-1.jsonld"],"body":{"kind":"trace","note":"The abstract states the two steps: a scan first, then vibrational frequencies.","proposer":{"model":{"name":"example-model","parameters":"998b5161e39e01c8bc43ef81ec89141e15b7ea53b2e936707920a6097dbcb2c5","processor":"local","prompt":"9cc338fba7c62db7266f530cb555a5ee36910d41a0120a3fe7fd7de1944d0931","provider":"Example Models (illustrative)","version":"2026-09","weights":"504b2d340045b92c22682b60020c21103aae7ff41172bb632fbe305eb4af9b5e"},"rank":1,"run":"illustrative-run-2","score":"0.8731"},"reference":"ebcq5cy","selector":[{"anchor":{"doc":"hale2026harmonic-damb","format":"scienting-anchor/1","kind":"sentence","node":"mhp0at8","node_hash":"ee42c4780724a411697e3a95daf182d37c792d4689a7172f32929785d5fd968e","own_hash":"24cc437784bf358d639755dbf59984ad2c0c7a0e7f9448b23e965039b67f759b","quote":{"exact":"in two steps","prefix":"bond constants for twelve bonds ","suffix":", first to a scan and then to vi"},"rev":12,"span":[48,60],"store":"smithlab"},"type":"AnchorSelector"},{"exact":"in two steps","prefix":"bond constants for twelve bonds ","suffix":", first to a scan and then to vi","type":"TextQuoteSelector"}],"source":"tag:scient.ing,2026:doc:smithlab/hale2026harmonic-damb","type":"SpecificResource","verdict":"supports"},"created":"2026-10-12T09:00:00Z","creator":{"id":"tag:scient.ing,2026:software:example-model","name":"A model (illustrative)","type":"Software"},"id":"tag:scient.ing,2026:annotation:smithlab/jh0e4st2c0kpt","layer":"tag:scient.ing,2026:layer:smithlab/tr6c3q8m","motivation":"linking","receipt":"qvckzaatz5d1v","status":"confirmed","target":{"selector":[{"anchor":{"doc":"writer2027fixed-ynr7.v3","format":"scienting-anchor/1","kind":"sentence","node":"qgh1k31","node_hash":"93a39490870e694299e88e6f475d42c38078d1442959e555bdbdf58488dcf187","own_hash":"e004436e7b28ff3dc41e0056f231ba707d9be985694e68acb911345a547842ed","quote":{"exact":"two-step fit","prefix":"Its constants come from a ","suffix":" (Hale and Osei, Table 1).1"},"rev":0,"span":[26,38],"store":"smithlab"},"type":"AnchorSelector"},{"exact":"two-step fit","prefix":"Its constants come from a ","suffix":" (Hale and Osei, Table 1).1","type":"TextQuoteSelector"}],"source":"tag:scient.ing,2026:doc:smithlab/writer2027fixed-ynr7.v3","type":"SpecificResource"},"type":"Annotation"},"cited":"fixtures/packages/hale2026harmonic-damb","cited_corrections":[],"citing":"fixtures/packages/writer2027fixed-ynr7.v3","citing_corrections":[{"at":"2026-10-13T12:00:00Z","author":{"key":"ana"},"evidence":[{"by":"ana","kind":"person"}],"op":"relink","reason":"Vector.","refs":null,"run":3,"target":"qgh1k31"},{"at":"2026-10-13T12:00:00Z","author":{"key":"ana"},"evidence":[{"by":"ana","kind":"person"}],"op":"resolve","reason":"Vector.","resolution":{"doi":"10.5555/example.other.2020","type":"doi"},"target":"ebcq5cy"}],"expect":{"cited":{"node":"mhp0at8","node_hash":"ee42c4780724a411697e3a95daf182d37c792d4689a7172f32929785d5fd968e","result":"current","span":[48,60]},"cites":[],"citing":{"node":"qgh1k31","node_hash":"401c7460dc29e50f3e6ecd8968118b41d7cfd8be24c81e6ef388eb66c7361044","result":"reattached","span":[26,38]},"open":"smithlab/hale2026harmonic-damb#mhp0at8@ee42c4[48:60]","reason":"the sentence no longer cites the reference","reference":"ebcq5cy","resolution":{"doi":"10.5555/example.other.2020","type":"doi"},"result":"unlinked"},"id":"unlinked-before-misresolved","op":"resolve","same_as":[]}
|
||||
{"annotation":{"@context":["http://www.w3.org/ns/anno.jsonld","https://paperspec.scient.ing/context/scienting-1.jsonld"],"body":{"kind":"trace","note":"The abstract states the two steps: a scan first, then vibrational frequencies.","proposer":{"model":{"name":"example-model","parameters":"998b5161e39e01c8bc43ef81ec89141e15b7ea53b2e936707920a6097dbcb2c5","processor":"local","prompt":"9cc338fba7c62db7266f530cb555a5ee36910d41a0120a3fe7fd7de1944d0931","provider":"Example Models (illustrative)","version":"2026-09","weights":"504b2d340045b92c22682b60020c21103aae7ff41172bb632fbe305eb4af9b5e"},"rank":1,"run":"illustrative-run-2","score":"0.8731"},"reference":"ebcq5cy","selector":[{"anchor":{"doc":"hale2026harmonic-damb","format":"scienting-anchor/1","kind":"sentence","node":"mhp0at8","node_hash":"ee42c4780724a411697e3a95daf182d37c792d4689a7172f32929785d5fd968e","own_hash":"24cc437784bf358d639755dbf59984ad2c0c7a0e7f9448b23e965039b67f759b","quote":{"exact":"in two steps","prefix":"bond constants for twelve bonds ","suffix":", first to a scan and then to vi"},"rev":12,"span":[48,60],"store":"smithlab"},"type":"AnchorSelector"},{"exact":"in two steps","prefix":"bond constants for twelve bonds ","suffix":", first to a scan and then to vi","type":"TextQuoteSelector"}],"source":"tag:scient.ing,2026:doc:smithlab/hale2026harmonic-damb","type":"SpecificResource","verdict":"supports"},"created":"2026-10-12T09:00:00Z","creator":{"id":"tag:scient.ing,2026:software:example-model","name":"A model (illustrative)","type":"Software"},"id":"tag:scient.ing,2026:annotation:smithlab/jh0e4st2c0kpt","layer":"tag:scient.ing,2026:layer:smithlab/tr6c3q8m","motivation":"linking","receipt":"qvckzaatz5d1v","status":"confirmed","target":{"selector":[{"anchor":{"doc":"writer2027fixed-ynr7.v3","format":"scienting-anchor/1","kind":"sentence","node":"qgh1k31","node_hash":"93a39490870e694299e88e6f475d42c38078d1442959e555bdbdf58488dcf187","own_hash":"e004436e7b28ff3dc41e0056f231ba707d9be985694e68acb911345a547842ed","quote":{"exact":"two-step fit","prefix":"Its constants come from a ","suffix":" (Hale and Osei, Table 1).1"},"rev":0,"span":[26,38],"store":"smithlab"},"type":"AnchorSelector"},{"exact":"two-step fit","prefix":"Its constants come from a ","suffix":" (Hale and Osei, Table 1).1","type":"TextQuoteSelector"}],"source":"tag:scient.ing,2026:doc:smithlab/writer2027fixed-ynr7.v3","type":"SpecificResource"},"type":"Annotation"},"cited":"fixtures/packages/hale2026harmonic-damb","citing":"fixtures/packages/writer2027fixed-ynr7.v3","expect":[],"id":"problems-none","op":"problems"}
|
||||
{"annotation":{"@context":["http://www.w3.org/ns/anno.jsonld","https://paperspec.scient.ing/context/scienting-1.jsonld"],"body":{"kind":"trace","note":"Table 1 lists the fitted constants of both parameter sets.","reference":"ebcq5cy","selector":[{"anchor":{"doc":"hale2026harmonic-damb","format":"scienting-anchor/1","kind":"table","node":"hj4s7pb","node_hash":"6c6fba200faf41cd06fa8b64e06e2aa29ff8848568b859ae42d1902596348643","own_hash":"66310a9ab1c92568f4c6cae012e35d4245d4ca4b89d2542b7c2113a31ad379ed","rev":12,"store":"smithlab"},"type":"AnchorSelector"}],"source":"tag:scient.ing,2026:doc:smithlab/hale2026harmonic-damb","type":"SpecificResource","verdict":"supports"},"created":"2026-10-12T09:10:00Z","creator":{"id":"tag:scient.ing,2026:key:ana","name":"Ana Example","type":"Person"},"id":"tag:scient.ing,2026:annotation:smithlab/fhnynfb8jx0wc","layer":"tag:scient.ing,2026:layer:smithlab/tr6c3q8m","motivation":"linking","receipt":"qvckzaatz5d1v","status":"confirmed","target":{"selector":[{"anchor":{"doc":"writer2027fixed-ynr7.v3","format":"scienting-anchor/1","kind":"sentence","node":"qgh1k31","node_hash":"93a39490870e694299e88e6f475d42c38078d1442959e555bdbdf58488dcf187","own_hash":"e004436e7b28ff3dc41e0056f231ba707d9be985694e68acb911345a547842ed","quote":{"exact":"Its constants come from a two-step fit (Hale and Osei, Table 1).1"},"rev":0,"store":"smithlab"},"type":"AnchorSelector"},{"exact":"Its constants come from a two-step fit (Hale and Osei, Table 1).1","prefix":"","suffix":"","type":"TextQuoteSelector"}],"source":"tag:scient.ing,2026:doc:smithlab/writer2027fixed-ynr7.v3","type":"SpecificResource"},"type":"Annotation"},"cited":"fixtures/packages/hale2026harmonic-damb","citing":"fixtures/packages/writer2027fixed-ynr7.v3","expect":[],"id":"problems-table","op":"problems"}
|
||||
{"annotation":{"@context":["http://www.w3.org/ns/anno.jsonld","https://paperspec.scient.ing/context/scienting-1.jsonld"],"body":{"kind":"trace","note":"The abstract states the two steps: a scan first, then vibrational frequencies.","proposer":{"model":{"name":"example-model","parameters":"998b5161e39e01c8bc43ef81ec89141e15b7ea53b2e936707920a6097dbcb2c5","processor":"local","prompt":"9cc338fba7c62db7266f530cb555a5ee36910d41a0120a3fe7fd7de1944d0931","provider":"Example Models (illustrative)","version":"2026-09","weights":"504b2d340045b92c22682b60020c21103aae7ff41172bb632fbe305eb4af9b5e"},"rank":1,"run":"illustrative-run-2","score":"0.8731"},"reference":"ebcq5cy","selector":[{"anchor":{"doc":"hale2026harmonic-damb","format":"scienting-anchor/1","kind":"sentence","node":"mhp0at8","node_hash":"ee42c4780724a411697e3a95daf182d37c792d4689a7172f32929785d5fd968e","own_hash":"24cc437784bf358d639755dbf59984ad2c0c7a0e7f9448b23e965039b67f759b","quote":{"exact":"in two steps","prefix":"bond constants for twelve bonds ","suffix":", first to a scan and then to vi"},"rev":12,"span":[48,60],"store":"smithlab"},"type":"AnchorSelector"},{"exact":"in two steps","prefix":"bond constants for twelve bonds ","suffix":", first to a scan and then to vi","type":"TextQuoteSelector"}],"source":"tag:scient.ing,2026:doc:smithlab/okafor2025solvent-acth","type":"SpecificResource","verdict":"supports"},"created":"2026-10-12T09:00:00Z","creator":{"id":"tag:scient.ing,2026:software:example-model","name":"A model (illustrative)","type":"Software"},"id":"tag:scient.ing,2026:annotation:smithlab/jh0e4st2c0kpt","layer":"tag:scient.ing,2026:layer:smithlab/tr6c3q8m","motivation":"linking","receipt":"qvckzaatz5d1v","status":"confirmed","target":{"selector":[{"anchor":{"doc":"writer2027fixed-ynr7.v3","format":"scienting-anchor/1","kind":"sentence","node":"qgh1k31","node_hash":"93a39490870e694299e88e6f475d42c38078d1442959e555bdbdf58488dcf187","own_hash":"e004436e7b28ff3dc41e0056f231ba707d9be985694e68acb911345a547842ed","quote":{"exact":"two-step fit","prefix":"Its constants come from a ","suffix":" (Hale and Osei, Table 1).1"},"rev":0,"span":[26,38],"store":"smithlab"},"type":"AnchorSelector"},{"exact":"two-step fit","prefix":"Its constants come from a ","suffix":" (Hale and Osei, Table 1).1","type":"TextQuoteSelector"}],"source":"tag:scient.ing,2026:doc:smithlab/writer2027fixed-ynr7.v3","type":"SpecificResource"},"type":"Annotation"},"cited":"fixtures/packages/hale2026harmonic-damb","citing":"fixtures/packages/writer2027fixed-ynr7.v3","expect":[{"message":"an anchor of the selector is in hale2026harmonic-damb, not in the source smithlab/okafor2025solvent-acth","rule":"layer.trace.source"}],"id":"source-is-another-document","op":"problems"}
|
||||
{"annotation":{"@context":["http://www.w3.org/ns/anno.jsonld","https://paperspec.scient.ing/context/scienting-1.jsonld"],"body":{"kind":"trace","note":"The abstract states the two steps: a scan first, then vibrational frequencies.","proposer":{"model":{"name":"example-model","parameters":"998b5161e39e01c8bc43ef81ec89141e15b7ea53b2e936707920a6097dbcb2c5","processor":"local","prompt":"9cc338fba7c62db7266f530cb555a5ee36910d41a0120a3fe7fd7de1944d0931","provider":"Example Models (illustrative)","version":"2026-09","weights":"504b2d340045b92c22682b60020c21103aae7ff41172bb632fbe305eb4af9b5e"},"rank":1,"run":"illustrative-run-2","score":"0.8731"},"reference":"ebcq5cy","selector":[{"anchor":{"doc":"writer2027fixed-ynr7.v3","format":"scienting-anchor/1","kind":"sentence","node":"qgh1k31","node_hash":"93a39490870e694299e88e6f475d42c38078d1442959e555bdbdf58488dcf187","own_hash":"e004436e7b28ff3dc41e0056f231ba707d9be985694e68acb911345a547842ed","quote":{"exact":"two-step fit","prefix":"Its constants come from a ","suffix":" (Hale and Osei, Table 1).1"},"rev":0,"span":[26,38],"store":"smithlab"},"type":"AnchorSelector"},{"exact":"two-step fit","prefix":"Its constants come from a ","suffix":" (Hale and Osei, Table 1).1","type":"TextQuoteSelector"}],"source":"tag:scient.ing,2026:doc:smithlab/writer2027fixed-ynr7.v3","type":"SpecificResource","verdict":"supports"},"created":"2026-10-12T09:00:00Z","creator":{"id":"tag:scient.ing,2026:software:example-model","name":"A model (illustrative)","type":"Software"},"id":"tag:scient.ing,2026:annotation:smithlab/jh0e4st2c0kpt","layer":"tag:scient.ing,2026:layer:smithlab/tr6c3q8m","motivation":"linking","receipt":"qvckzaatz5d1v","status":"confirmed","target":{"selector":[{"anchor":{"doc":"writer2027fixed-ynr7.v3","format":"scienting-anchor/1","kind":"sentence","node":"qgh1k31","node_hash":"93a39490870e694299e88e6f475d42c38078d1442959e555bdbdf58488dcf187","own_hash":"e004436e7b28ff3dc41e0056f231ba707d9be985694e68acb911345a547842ed","quote":{"exact":"two-step fit","prefix":"Its constants come from a ","suffix":" (Hale and Osei, Table 1).1"},"rev":0,"span":[26,38],"store":"smithlab"},"type":"AnchorSelector"},{"exact":"two-step fit","prefix":"Its constants come from a ","suffix":" (Hale and Osei, Table 1).1","type":"TextQuoteSelector"}],"source":"tag:scient.ing,2026:doc:smithlab/writer2027fixed-ynr7.v3","type":"SpecificResource"},"type":"Annotation"},"cited":null,"citing":"fixtures/packages/writer2027fixed-ynr7.v3","expect":[{"message":"the source is the citing document itself; a trace leads to another paper","rule":"layer.trace.source"}],"id":"source-is-the-citing-document","op":"problems"}
|
||||
{"annotation":{"@context":["http://www.w3.org/ns/anno.jsonld","https://paperspec.scient.ing/context/scienting-1.jsonld"],"body":{"kind":"trace","note":"The abstract states the two steps: a scan first, then vibrational frequencies.","proposer":{"model":{"name":"example-model","parameters":"998b5161e39e01c8bc43ef81ec89141e15b7ea53b2e936707920a6097dbcb2c5","processor":"local","prompt":"9cc338fba7c62db7266f530cb555a5ee36910d41a0120a3fe7fd7de1944d0931","provider":"Example Models (illustrative)","version":"2026-09","weights":"504b2d340045b92c22682b60020c21103aae7ff41172bb632fbe305eb4af9b5e"},"rank":1,"run":"illustrative-run-2","score":"0.8731"},"reference":"ebcq5cy","selector":[{"exact":"in two steps","prefix":"bond constants for twelve bonds ","suffix":", first to a scan and then to vi","type":"TextQuoteSelector"}],"source":"tag:scient.ing,2026:doc:smithlab/hale2026harmonic-damb","type":"SpecificResource","verdict":"supports"},"created":"2026-10-12T09:00:00Z","creator":{"id":"tag:scient.ing,2026:software:example-model","name":"A model (illustrative)","type":"Software"},"id":"tag:scient.ing,2026:annotation:smithlab/jh0e4st2c0kpt","layer":"tag:scient.ing,2026:layer:smithlab/tr6c3q8m","motivation":"linking","receipt":"qvckzaatz5d1v","status":"confirmed","target":{"selector":[{"anchor":{"doc":"writer2027fixed-ynr7.v3","format":"scienting-anchor/1","kind":"sentence","node":"qgh1k31","node_hash":"93a39490870e694299e88e6f475d42c38078d1442959e555bdbdf58488dcf187","own_hash":"e004436e7b28ff3dc41e0056f231ba707d9be985694e68acb911345a547842ed","quote":{"exact":"two-step fit","prefix":"Its constants come from a ","suffix":" (Hale and Osei, Table 1).1"},"rev":0,"span":[26,38],"store":"smithlab"},"type":"AnchorSelector"},{"exact":"two-step fit","prefix":"Its constants come from a ","suffix":" (Hale and Osei, Table 1).1","type":"TextQuoteSelector"}],"source":"tag:scient.ing,2026:doc:smithlab/writer2027fixed-ynr7.v3","type":"SpecificResource"},"type":"Annotation"},"cited":null,"citing":"fixtures/packages/writer2027fixed-ynr7.v3","expect":[{"message":"the body's selector has no AnchorSelector","rule":"layer.trace.source"}],"id":"source-without-anchor","op":"problems"}
|
||||
{"annotation":{"@context":["http://www.w3.org/ns/anno.jsonld","https://paperspec.scient.ing/context/scienting-1.jsonld"],"body":{"kind":"trace","note":"The abstract states the two steps: a scan first, then vibrational frequencies.","proposer":{"model":{"name":"example-model","parameters":"998b5161e39e01c8bc43ef81ec89141e15b7ea53b2e936707920a6097dbcb2c5","processor":"local","prompt":"9cc338fba7c62db7266f530cb555a5ee36910d41a0120a3fe7fd7de1944d0931","provider":"Example Models (illustrative)","version":"2026-09","weights":"504b2d340045b92c22682b60020c21103aae7ff41172bb632fbe305eb4af9b5e"},"rank":1,"run":"illustrative-run-2","score":"0.8731"},"reference":"ebcq5cy","selector":[{"anchor":{"doc":"hale2026harmonic-damb","format":"scienting-anchor/1","kind":"sentence","node":"mhp0at8","node_hash":"ee42c4780724a411697e3a95daf182d37c792d4689a7172f32929785d5fd968e","own_hash":"24cc437784bf358d639755dbf59984ad2c0c7a0e7f9448b23e965039b67f759b","quote":{"exact":"in two steps","prefix":"bond constants for twelve bonds ","suffix":", first to a scan and then to vi"},"rev":12,"span":[48,60],"store":"smithlab"},"type":"AnchorSelector"},{"exact":"in three steps","prefix":"bond constants for twelve bonds ","suffix":", first to a scan and then to vi","type":"TextQuoteSelector"}],"source":"tag:scient.ing,2026:doc:smithlab/hale2026harmonic-damb","type":"SpecificResource","verdict":"supports"},"created":"2026-10-12T09:00:00Z","creator":{"id":"tag:scient.ing,2026:software:example-model","name":"A model (illustrative)","type":"Software"},"id":"tag:scient.ing,2026:annotation:smithlab/jh0e4st2c0kpt","layer":"tag:scient.ing,2026:layer:smithlab/tr6c3q8m","motivation":"linking","receipt":"qvckzaatz5d1v","status":"confirmed","target":{"selector":[{"anchor":{"doc":"writer2027fixed-ynr7.v3","format":"scienting-anchor/1","kind":"sentence","node":"qgh1k31","node_hash":"93a39490870e694299e88e6f475d42c38078d1442959e555bdbdf58488dcf187","own_hash":"e004436e7b28ff3dc41e0056f231ba707d9be985694e68acb911345a547842ed","quote":{"exact":"two-step fit","prefix":"Its constants come from a ","suffix":" (Hale and Osei, Table 1).1"},"rev":0,"span":[26,38],"store":"smithlab"},"type":"AnchorSelector"},{"exact":"two-step fit","prefix":"Its constants come from a ","suffix":" (Hale and Osei, Table 1).1","type":"TextQuoteSelector"}],"source":"tag:scient.ing,2026:doc:smithlab/writer2027fixed-ynr7.v3","type":"SpecificResource"},"type":"Annotation"},"cited":"fixtures/packages/hale2026harmonic-damb","citing":"fixtures/packages/writer2027fixed-ynr7.v3","expect":[{"message":"the TextQuoteSelector does not repeat the anchor's plain quote","rule":"layer.trace.source"}],"id":"source-quote-differs","op":"problems"}
|
||||
{"annotation":{"@context":["http://www.w3.org/ns/anno.jsonld","https://paperspec.scient.ing/context/scienting-1.jsonld"],"body":{"kind":"trace","note":"The abstract states the two steps: a scan first, then vibrational frequencies.","proposer":{"model":{"name":"example-model","parameters":"998b5161e39e01c8bc43ef81ec89141e15b7ea53b2e936707920a6097dbcb2c5","processor":"local","prompt":"9cc338fba7c62db7266f530cb555a5ee36910d41a0120a3fe7fd7de1944d0931","provider":"Example Models (illustrative)","version":"2026-09","weights":"504b2d340045b92c22682b60020c21103aae7ff41172bb632fbe305eb4af9b5e"},"rank":1,"run":"illustrative-run-2","score":"0.8731"},"reference":"ebcq5cy","selector":[{"anchor":{"doc":"hale2026harmonic-damb","format":"scienting-anchor/1","kind":"sentence","node":"zzzzzzz","node_hash":"ee42c4780724a411697e3a95daf182d37c792d4689a7172f32929785d5fd968e","own_hash":"24cc437784bf358d639755dbf59984ad2c0c7a0e7f9448b23e965039b67f759b","quote":{"exact":"in two steps","prefix":"bond constants for twelve bonds ","suffix":", first to a scan and then to vi"},"rev":12,"span":[48,60],"store":"smithlab"},"type":"AnchorSelector"}],"source":"tag:scient.ing,2026:doc:smithlab/hale2026harmonic-damb","type":"SpecificResource","verdict":"supports"},"created":"2026-10-12T09:00:00Z","creator":{"id":"tag:scient.ing,2026:software:example-model","name":"A model (illustrative)","type":"Software"},"id":"tag:scient.ing,2026:annotation:smithlab/jh0e4st2c0kpt","layer":"tag:scient.ing,2026:layer:smithlab/tr6c3q8m","motivation":"linking","receipt":"qvckzaatz5d1v","status":"confirmed","target":{"selector":[{"anchor":{"doc":"writer2027fixed-ynr7.v3","format":"scienting-anchor/1","kind":"sentence","node":"qgh1k31","node_hash":"93a39490870e694299e88e6f475d42c38078d1442959e555bdbdf58488dcf187","own_hash":"e004436e7b28ff3dc41e0056f231ba707d9be985694e68acb911345a547842ed","quote":{"exact":"two-step fit","prefix":"Its constants come from a ","suffix":" (Hale and Osei, Table 1).1"},"rev":0,"span":[26,38],"store":"smithlab"},"type":"AnchorSelector"},{"exact":"two-step fit","prefix":"Its constants come from a ","suffix":" (Hale and Osei, Table 1).1","type":"TextQuoteSelector"}],"source":"tag:scient.ing,2026:doc:smithlab/writer2027fixed-ynr7.v3","type":"SpecificResource"},"type":"Annotation"},"cited":"fixtures/packages/hale2026harmonic-damb","citing":"fixtures/packages/writer2027fixed-ynr7.v3","expect":[{"message":"the passage's node zzzzzzz is not in the cited document","rule":"layer.trace.source"}],"id":"source-node-unknown","op":"problems"}
|
||||
{"annotation":{"@context":["http://www.w3.org/ns/anno.jsonld","https://paperspec.scient.ing/context/scienting-1.jsonld"],"body":{"kind":"trace","note":"The abstract states the two steps: a scan first, then vibrational frequencies.","proposer":{"model":{"name":"example-model","parameters":"998b5161e39e01c8bc43ef81ec89141e15b7ea53b2e936707920a6097dbcb2c5","processor":"local","prompt":"9cc338fba7c62db7266f530cb555a5ee36910d41a0120a3fe7fd7de1944d0931","provider":"Example Models (illustrative)","version":"2026-09","weights":"504b2d340045b92c22682b60020c21103aae7ff41172bb632fbe305eb4af9b5e"},"rank":1,"run":"illustrative-run-2","score":"0.8731"},"reference":"ebcq5cy","selector":[{"anchor":{"doc":"hale2026harmonic-damb","format":"scienting-anchor/1","kind":"sentence","node":"mhp0at8","node_hash":"ee42c4780724a411697e3a95daf182d37c792d4689a7172f32929785d5fd968e","own_hash":"24cc437784bf358d639755dbf59984ad2c0c7a0e7f9448b23e965039b67f759b","quote":{"exact":"in two steps","prefix":"bond constants for twelve bonds ","suffix":", first to a scan and then to vi"},"rev":12,"span":[48,60],"store":"smithlab"},"type":"AnchorSelector"},{"exact":"in two steps","prefix":"bond constants for twelve bonds ","suffix":", first to a scan and then to vi","type":"TextQuoteSelector"}],"source":"tag:scient.ing,2026:doc:smithlab/hale2026harmonic-damb","type":"SpecificResource","verdict":"supports"},"created":"2026-10-12T09:00:00Z","creator":{"id":"tag:scient.ing,2026:software:example-model","name":"A model (illustrative)","type":"Software"},"id":"tag:scient.ing,2026:annotation:smithlab/jh0e4st2c0kpt","layer":"tag:scient.ing,2026:layer:smithlab/tr6c3q8m","motivation":"linking","receipt":"qvckzaatz5d1v","status":"confirmed","target":{"selector":[{"anchor":{"doc":"writer2027fixed-ynr7.v3","format":"scienting-anchor/1","kind":"paragraph","leaves":[["kda5wym","8f22f3f58beb618723a14dd8fee8caaeb5cef6b26f43d88a5ddb4ff42f86d63e"],["qgh1k31","fe82fa8560942e052e31d9b18cc8d3a9ba54ce01d91b66538ded6ff682a4e885"],["wbntyy4","9b297e00b7348c2df4c6b12f67b2f879217a0d71e1be382fc8a4f6bd7439ddd0"]],"node":"x9ryw92","node_hash":"b17dff0cff3ce730e52efca92b9fe585ffc956cc9f76e52a8b37721cbbec442f","own_hash":"494e069be36a18ecaabf5a63be7c31617ade7a5460218ca83a2de3d8d8f964e0","rev":0,"store":"smithlab"},"type":"AnchorSelector"}],"source":"tag:scient.ing,2026:doc:smithlab/writer2027fixed-ynr7.v3","type":"SpecificResource"},"type":"Annotation"},"cited":"fixtures/packages/hale2026harmonic-damb","citing":"fixtures/packages/writer2027fixed-ynr7.v3","expect":[{"message":"the target anchor is not on a leaf with runs, whole or in part","rule":"layer.trace.target"}],"id":"target-is-a-paragraph","op":"problems"}
|
||||
{"annotation":{"@context":["http://www.w3.org/ns/anno.jsonld","https://paperspec.scient.ing/context/scienting-1.jsonld"],"body":{"kind":"trace","note":"The abstract states the two steps: a scan first, then vibrational frequencies.","proposer":{"model":{"name":"example-model","parameters":"998b5161e39e01c8bc43ef81ec89141e15b7ea53b2e936707920a6097dbcb2c5","processor":"local","prompt":"9cc338fba7c62db7266f530cb555a5ee36910d41a0120a3fe7fd7de1944d0931","provider":"Example Models (illustrative)","version":"2026-09","weights":"504b2d340045b92c22682b60020c21103aae7ff41172bb632fbe305eb4af9b5e"},"rank":1,"run":"illustrative-run-2","score":"0.8731"},"reference":"ebcq5cy","selector":[{"anchor":{"doc":"hale2026harmonic-damb","format":"scienting-anchor/1","kind":"sentence","node":"mhp0at8","node_hash":"ee42c4780724a411697e3a95daf182d37c792d4689a7172f32929785d5fd968e","own_hash":"24cc437784bf358d639755dbf59984ad2c0c7a0e7f9448b23e965039b67f759b","quote":{"exact":"in two steps","prefix":"bond constants for twelve bonds ","suffix":", first to a scan and then to vi"},"rev":12,"span":[48,60],"store":"smithlab"},"type":"AnchorSelector"},{"exact":"in two steps","prefix":"bond constants for twelve bonds ","suffix":", first to a scan and then to vi","type":"TextQuoteSelector"}],"source":"tag:scient.ing,2026:doc:smithlab/hale2026harmonic-damb","type":"SpecificResource","verdict":"supports"},"created":"2026-10-12T09:00:00Z","creator":{"id":"tag:scient.ing,2026:software:example-model","name":"A model (illustrative)","type":"Software"},"id":"tag:scient.ing,2026:annotation:smithlab/jh0e4st2c0kpt","layer":"tag:scient.ing,2026:layer:smithlab/tr6c3q8m","motivation":"linking","receipt":"qvckzaatz5d1v","status":"confirmed","target":{"selector":[{"anchor":{"doc":"writer2027fixed-ynr7.v3","format":"scienting-anchor/1","kind":"sentence","node":"kda5wym","node_hash":"dd438a68616e63fd1cd5050d308779af58dfed2b4d43cc5a4bbab3fc8d4f2bcb","own_hash":"46dfbddc0e456a2b7f4105b8d3636220ff9145acccf3bdf81a206f0b84f95b63","quote":{"exact":"The bond term is harmonic in the bond length."},"rev":0,"store":"smithlab"},"type":"AnchorSelector"},{"exact":"The bond term is harmonic in the bond length.","prefix":"","suffix":"","type":"TextQuoteSelector"}],"source":"tag:scient.ing,2026:doc:smithlab/writer2027fixed-ynr7.v3","type":"SpecificResource"},"type":"Annotation"},"cited":"fixtures/packages/hale2026harmonic-damb","citing":"fixtures/packages/writer2027fixed-ynr7.v3","expect":[{"message":"the sentence does not cite the reference ebcq5cy","rule":"layer.trace.target"}],"id":"target-cites-nothing","op":"problems"}
|
||||
{"annotation":{"@context":["http://www.w3.org/ns/anno.jsonld","https://paperspec.scient.ing/context/scienting-1.jsonld"],"body":{"kind":"trace","note":"The abstract states the two steps: a scan first, then vibrational frequencies.","proposer":{"model":{"name":"example-model","parameters":"998b5161e39e01c8bc43ef81ec89141e15b7ea53b2e936707920a6097dbcb2c5","processor":"local","prompt":"9cc338fba7c62db7266f530cb555a5ee36910d41a0120a3fe7fd7de1944d0931","provider":"Example Models (illustrative)","version":"2026-09","weights":"504b2d340045b92c22682b60020c21103aae7ff41172bb632fbe305eb4af9b5e"},"rank":1,"run":"illustrative-run-2","score":"0.8731"},"reference":"qgh1k31","selector":[{"anchor":{"doc":"hale2026harmonic-damb","format":"scienting-anchor/1","kind":"sentence","node":"mhp0at8","node_hash":"ee42c4780724a411697e3a95daf182d37c792d4689a7172f32929785d5fd968e","own_hash":"24cc437784bf358d639755dbf59984ad2c0c7a0e7f9448b23e965039b67f759b","quote":{"exact":"in two steps","prefix":"bond constants for twelve bonds ","suffix":", first to a scan and then to vi"},"rev":12,"span":[48,60],"store":"smithlab"},"type":"AnchorSelector"},{"exact":"in two steps","prefix":"bond constants for twelve bonds ","suffix":", first to a scan and then to vi","type":"TextQuoteSelector"}],"source":"tag:scient.ing,2026:doc:smithlab/hale2026harmonic-damb","type":"SpecificResource","verdict":"supports"},"created":"2026-10-12T09:00:00Z","creator":{"id":"tag:scient.ing,2026:software:example-model","name":"A model (illustrative)","type":"Software"},"id":"tag:scient.ing,2026:annotation:smithlab/jh0e4st2c0kpt","layer":"tag:scient.ing,2026:layer:smithlab/tr6c3q8m","motivation":"linking","receipt":"qvckzaatz5d1v","status":"confirmed","target":{"selector":[{"anchor":{"doc":"writer2027fixed-ynr7.v3","format":"scienting-anchor/1","kind":"sentence","node":"qgh1k31","node_hash":"93a39490870e694299e88e6f475d42c38078d1442959e555bdbdf58488dcf187","own_hash":"e004436e7b28ff3dc41e0056f231ba707d9be985694e68acb911345a547842ed","quote":{"exact":"two-step fit","prefix":"Its constants come from a ","suffix":" (Hale and Osei, Table 1).1"},"rev":0,"span":[26,38],"store":"smithlab"},"type":"AnchorSelector"},{"exact":"two-step fit","prefix":"Its constants come from a ","suffix":" (Hale and Osei, Table 1).1","type":"TextQuoteSelector"}],"source":"tag:scient.ing,2026:doc:smithlab/writer2027fixed-ynr7.v3","type":"SpecificResource"},"type":"Annotation"},"cited":"fixtures/packages/hale2026harmonic-damb","citing":"fixtures/packages/writer2027fixed-ynr7.v3","expect":[{"message":"the reference qgh1k31 is not a reference node of the citing document","rule":"layer.trace.target"}],"id":"target-reference-is-a-sentence","op":"problems"}
|
||||
{"annotation":{"@context":["http://www.w3.org/ns/anno.jsonld","https://paperspec.scient.ing/context/scienting-1.jsonld"],"body":{"kind":"trace","note":"The abstract states the two steps: a scan first, then vibrational frequencies.","proposer":{"model":{"name":"example-model","parameters":"998b5161e39e01c8bc43ef81ec89141e15b7ea53b2e936707920a6097dbcb2c5","processor":"local","prompt":"9cc338fba7c62db7266f530cb555a5ee36910d41a0120a3fe7fd7de1944d0931","provider":"Example Models (illustrative)","version":"2026-09","weights":"504b2d340045b92c22682b60020c21103aae7ff41172bb632fbe305eb4af9b5e"},"rank":1,"run":"illustrative-run-2","score":"0.8731"},"reference":"ebcq5cy","selector":[{"anchor":{"doc":"hale2026harmonic-damb","format":"scienting-anchor/1","kind":"sentence","node":"mhp0at8","node_hash":"ee42c4780724a411697e3a95daf182d37c792d4689a7172f32929785d5fd968e","own_hash":"24cc437784bf358d639755dbf59984ad2c0c7a0e7f9448b23e965039b67f759b","quote":{"exact":"in two steps","prefix":"bond constants for twelve bonds ","suffix":", first to a scan and then to vi"},"rev":12,"span":[48,60],"store":"smithlab"},"type":"AnchorSelector"},{"exact":"in two steps","prefix":"bond constants for twelve bonds ","suffix":", first to a scan and then to vi","type":"TextQuoteSelector"}],"source":"tag:scient.ing,2026:doc:smithlab/hale2026harmonic-damb","type":"SpecificResource","verdict":"supports"},"created":"2026-10-12T09:00:00Z","creator":{"id":"tag:scient.ing,2026:software:example-model","name":"A model (illustrative)","type":"Software"},"id":"tag:scient.ing,2026:annotation:smithlab/jh0e4st2c0kpt","layer":"tag:scient.ing,2026:layer:smithlab/tr6c3q8m","motivation":"linking","receipt":"qvckzaatz5d1v","status":"confirmed","target":{"source":"tag:scient.ing,2026:annotation:smithlab/arm3zmj6be9fz","type":"SpecificResource"},"type":"Annotation"},"cited":"fixtures/packages/hale2026harmonic-damb","citing":"fixtures/packages/writer2027fixed-ynr7.v3","expect":[{"message":"the target is not a document selected by an AnchorSelector","rule":"layer.trace.target"}],"id":"target-is-a-reply","op":"problems"}
|
||||
Reference in new issue
Block a user