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:
alexmandClaude committed 2026-10-17 20:12:04 -04:00
1 parent 44a8e3f07c
commit 7890fa1ad7
25 files changed
+966 -50

No files matched your search

+15
View File
@@ -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.