linked-rolls 0.75.0 → 0.76.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -132,22 +132,20 @@ edits the editor wrote, which hold their versions to what they insert
132
132
  and delete. Motivations, beliefs and versions known only from a
133
133
  recording remain the editor's to state.
134
134
 
135
- What the program concluded is recorded as a program's conclusion. A
136
- search scores whole stemmata, so the derivations and edits it states
137
- are one proposition set (CRMinf I4) held under one belief. Each of
138
- them names the set in its annotation, and the set states the belief
139
- once. The belief, likely unless the editor says otherwise, rests on an
140
- inference that names this library and its release, the copies it
141
- collated, the published rules its costs carry out (`stemmaRules`) and,
142
- in its note, the costs and settings it ran with and the margin of each
143
- grouping. Who ran it and when are the editor's to give. A derivation or
144
- an edit the editor holds for reasons of their own stays theirs, outside
145
- the set.
135
+ What the program concluded is stated plainly, as an edition states its
136
+ stemma. The edition's creation names how it was found: the published
137
+ rules the costs carry out among its principles (`stemmaRules`), and
138
+ this release of the library among its software, which finds the
139
+ stemma again with the defaults it has. A derivation the editor holds
140
+ under a belief of their own, and an edit that says why it was made,
141
+ stay as they are. Among the versions, derivations and edits, whatever
142
+ carries neither a belief nor a motivation of the editor's is the
143
+ program's.
146
144
 
147
145
  ```ts
148
- const run = { actor: editor, date: { within: new Date() } }
149
- const adopted = produce(edition, adoptStemma(proposal, run))
150
- adopted.propositionSets // [{ type: 'propositionSet', id, belief: { certainty: 'likely', reasons: [{ type: 'inference', software: [linkedRolls], … }] } }]
146
+ const adopted = produce(edition, adoptStemma(proposal))
147
+ adopted.creation.principles // ['https://welte225.org/rules#shared-addition', …]
148
+ adopted.creation.software // [{ name: 'linked-rolls', version: '0.76.0' }]
151
149
  ```
152
150
 
153
151
  The costs are those with which the stemma of WM 225 comes out of its
@@ -157,28 +155,26 @@ principles confirm it.
157
155
 
158
156
  ## Proposing the edits
159
157
 
160
- What a version inserts and deletes can be grouped into edits and typed
161
- by their shape alone. `proposeEdits` reads a version's changes line by
162
- line, a latched function, a held one or a pitch, and never across a
163
- command both texts keep, so that an edit stays at one place. A function
164
- turned on and off again is one edit, typed an additional accent where
165
- the bar spells an accent so; a perforation put elsewhere is one move
166
- with the one it replaces, typed a shift, or shortened or prolonged
167
- where it begins about where the other did; overlapping notes are one
168
- edit, a note struck again or two tied; a held command standing for a
169
- latched pair is one recoding; a command the text makes no use of is a
170
- redundancy added or removed; cancels punched across lines at one place
171
- are one row. `adoptEdits` puts the proposal in place of the edits a
172
- collation or a program's run wrote and nobody has motivated, and keeps
173
- the rest. The edits it proposes are a proposition set of their own. The
174
- inference behind its belief names the beliefs the derivations of the
175
- versions are held with as its premises, so that edits grouped against
176
- a proposed stemma rest on it.
158
+ What a version inserts and deletes can be grouped into edits, and the
159
+ shape of each read off them alone. `proposeEdits` reads a version's
160
+ changes line by line, a latched function, a held one or a pitch, and
161
+ never across a command both texts keep, so that an edit stays at one
162
+ place. A function turned on and off again is one edit, an additional
163
+ accent where the bar spells an accent so; a perforation put elsewhere
164
+ is one move with the one it replaces, a shift, or shortened or
165
+ prolonged where it begins about where the other did; overlapping notes
166
+ are one edit, a note struck again or two tied; a held command standing
167
+ for a latched pair is one recoding; a command the text makes no use of
168
+ is a redundancy added or removed; cancels punched across lines at one
169
+ place are one row. `adoptEdits` puts the proposal in place of the
170
+ program's edits, those that say nothing of why they were made or only
171
+ that they are musical or technical changes, and keeps the rest. Its creation comes to
172
+ name the rule that keeps an edit at one place among its principles.
177
173
 
178
174
  ```ts
179
175
  import { adoptEdits, adoptStemma, proposeEdits, proposeStemma } from 'linked-rolls'
180
176
 
181
- proposeEdits(edition, versionId) // [{ insert, delete, editType, grouping }, …]
177
+ proposeEdits(edition, versionId) // [{ insert, delete, shape, grouping }, …]
182
178
 
183
179
  // The stemma and the edits of its versions, as one undo step
184
180
  const adopted = produce(edition, draft => {
@@ -187,14 +183,26 @@ const adopted = produce(edition, draft => {
187
183
  })
188
184
  ```
189
185
 
190
- Why an edit was made is not read off its shape, and neither is whether
191
- a shift corrects an error: what is left to the editor is the
192
- motivation. Correcting an error is therefore one of the motivations
193
- (`correctedError`), not one of the edit types, and a document written
194
- while it was a type is read with that motivation in its place. On
195
- WM 225 the proposal reproduces 370 of the 414 edits the editor wrote,
196
- from what each version changes, and every type the editor gave them.
197
- From the copies alone, through the proposed stemma, it reproduces 326.
186
+ Every change of a version that derives from another states a
187
+ motivation, and the motivation says what kind of change it is
188
+ (`kindOfChange`). A program states it from the shape of the change: a
189
+ redundancy cleaned up or added, or a command recoded for another
190
+ system, is a technical change (`technicalChange`), which the procedure
191
+ of the version's creation explains; an added accent, a shift, a
192
+ shortening or a prolongation is a musical change (`musicalChange`),
193
+ under the rule that later versions are the more refined. The editor
194
+ may say otherwise, and adopting the edits again keeps the kind an edit
195
+ over the same changes states. The editor speculates on the musical
196
+ changes only: a reason of their own, which the edition states once
197
+ (`Edition.motivations`) and any version's edits name, takes the place of
198
+ the generic musical motivation. The edits of a version that derives from none state its
199
+ text, and no motivation.
200
+
201
+ On WM 225 the proposal reproduces 601 of the 690 edits of the versions
202
+ that derive from another, from what each version changes, and from the
203
+ copies alone, through the proposed stemma, 554. It agrees with the
204
+ editor on what kind of change an edit is but for two, each a reason the
205
+ editor gives for what the program reads as a redundancy.
198
206
 
199
207
  ## Format
200
208
 
@@ -206,12 +214,10 @@ https://pfefferniels.github.io/linked-rolls/, to which the w3id.org
206
214
  identifiers redirect.
207
215
 
208
216
  Beliefs are written as JSON-LD-star annotations (`@annotation`), so you
209
- need a processor that supports JSON-LD-star to read them. A statement
210
- that belongs to a proposition set carries no belief of its own: its
211
- annotation names the set by its `@id`, which makes the set the reifier
212
- of every statement that names it, as RDF 1.2 allows. The set is stated
213
- once, under `propositionSets`, as a `crminf:I4_Proposition_Set` with its
214
- belief.
217
+ need a processor that supports JSON-LD-star to read them. They are kept
218
+ for what the editor doubts; everything else, the stemma and the edits a
219
+ program found among it, is stated plainly and reaches RDF with any
220
+ JSON-LD processor.
215
221
 
216
222
  ## Development
217
223
 
@@ -230,7 +236,7 @@ TypeScript 7 no longer has the compiler API it reads the source with.
230
236
 
231
237
  Raise the version in `package.json`, in `CITATION.cff`, there with the
232
238
  day as `date-released`, and in `src/software.ts`, which names the release
233
- in the reasonings the library records. Then commit, and push a tag
239
+ in the edition's creation when a proposal is adopted. Then commit, and push a tag
234
240
  `v<version>`. The `publish.yml` workflow builds, tests and publishes to
235
241
  npm, then makes a GitHub release of the tag, which Zenodo archives with
236
242
  a DOI. A test fails while the versions differ.
@@ -120,54 +120,20 @@ export type Collation = {
120
120
  */
121
121
  export declare const collationsOf: (own: readonly Readonly<AnySymbol>[], inherited: readonly Readonly<AnySymbol>[], locate: Locate, tolerance?: CollationTolerance) => Collation[];
122
122
  /**
123
- * The motivation a collation writes on what it makes, saying that
124
- * nobody has read it yet.
123
+ * Whether the edit is the program's: one that a collation or a proposal
124
+ * wrote and nobody has explained, so that collating again or proposing
125
+ * the edits again may write it anew. It carries no belief and names no
126
+ * motivation of the editor's: only the kind of change it is, the generic
127
+ * musical or technical one a program states (`musicalChange`,
128
+ * `technicalChange`), or none.
125
129
  *
126
- * A collation produces its edits by a rule, and an edition that
127
- * publishes them unmarked would present a machine's output as an
128
- * editor's reading. Stating that they are unchecked is the honest
129
- * middle: the readings are there to be seen and argued with, and a
130
- * reader can tell which of them anybody has weighed.
130
+ * Everything else is the editor's and stays: an edit that says why it
131
+ * was made, or that is held under a belief. A grouping of the editor's stays theirs once it says
132
+ * why; one that says nothing reads as the program's would.
131
133
  *
132
- * It does not make an edit the editor's. `isCollationsOwn` passes over
133
- * this motivation exactly, so a collation rewrites what it marked
134
- * before, and only what somebody has actually written stays.
135
- * @category Collation and alignment
136
- */
137
- export declare const unchecked = "unchecked";
138
- /**
139
- * The version's statement of what the unchecked motivation means, to stand in its `motivations`.
140
- * @category Collation and alignment
141
- */
142
- export declare const uncheckedMotivation: {
143
- readonly type: 'motivation';
144
- readonly id: "unchecked";
145
- readonly note: 'Made by collating the two texts and not yet read by an editor.';
146
- };
147
- /**
148
- * Whether the edit is one a collation writes by itself: a bare
149
- * insertion or a bare deletion saying nothing further, or a recoding
150
- * that exchanges two systems' spellings for one command.
151
- *
152
- * Collating again rewrites these and keeps the rest. An edit that says
153
- * what the change is, or why it was made, or what it rests on, is an
154
- * editor's reading of the difference between two texts, and collating
155
- * the two again is no reason to discard it.
156
- *
157
- * Such an exchange counts as the collation's own even where an editor
158
- * added a motivation to it, because it is derived rather than read:
159
- * freezing it would leave the transfers, where nearly every edit is
160
- * one, unable to be collated again at all. What an editor wrote on it
161
- * is not lost by that, since `connectVersions` keeps an exchange it
162
- * draws a second time over the very same symbols.
163
- *
164
- * A recoding that only inserts or only deletes is the editor's: a
165
- * collation types no one-sided edit, so one typed so is a reading of
166
- * the transfer that collating again must not undo.
167
- *
168
- * An edit a program proposed and typed is no reading of the editor's
169
- * either, as long as nobody has motivated it: it says so by belonging
170
- * to the proposition set the program's run concluded (`adoptEdits`).
134
+ * An exchange a collation draws again over the very same symbols is
135
+ * kept as it stands, with its identifier and whatever was written on
136
+ * it (`connectVersions`).
171
137
  * @category Collation and alignment
172
138
  */
173
139
  export declare const isCollationsOwn: (edit: Readonly<Edit>) => boolean;
@@ -1,4 +1,4 @@
1
- import { isHeldForItself, propositionSetOf } from "../model/Assumption.js";
1
+ import { musicalChange, technicalChange } from "../model/Edit.js";
2
2
  import { keyOf } from "../systems/TrackerBar.js";
3
3
  import { distance, mm } from "../model/Quantity.js";
4
4
  import { partitionPoint } from "../shared/sorted.js";
@@ -107,60 +107,22 @@ export const collationsOf = (own, inherited, locate, tolerance = defaultCollatio
107
107
  .sort((a, b) => a.index - b.index)
108
108
  .map(({ symbol: counterpart }) => ({ symbol, counterpart })));
109
109
  };
110
- /** The edit type a collation draws by itself, from the two systems' vocabularies rather than off the paper. */
111
- const drawnByCollation = 'recoding';
112
110
  /**
113
- * The motivation a collation writes on what it makes, saying that
114
- * nobody has read it yet.
111
+ * Whether the edit is the program's: one that a collation or a proposal
112
+ * wrote and nobody has explained, so that collating again or proposing
113
+ * the edits again may write it anew. It carries no belief and names no
114
+ * motivation of the editor's: only the kind of change it is, the generic
115
+ * musical or technical one a program states (`musicalChange`,
116
+ * `technicalChange`), or none.
115
117
  *
116
- * A collation produces its edits by a rule, and an edition that
117
- * publishes them unmarked would present a machine's output as an
118
- * editor's reading. Stating that they are unchecked is the honest
119
- * middle: the readings are there to be seen and argued with, and a
120
- * reader can tell which of them anybody has weighed.
118
+ * Everything else is the editor's and stays: an edit that says why it
119
+ * was made, or that is held under a belief. A grouping of the editor's stays theirs once it says
120
+ * why; one that says nothing reads as the program's would.
121
121
  *
122
- * It does not make an edit the editor's. `isCollationsOwn` passes over
123
- * this motivation exactly, so a collation rewrites what it marked
124
- * before, and only what somebody has actually written stays.
122
+ * An exchange a collation draws again over the very same symbols is
123
+ * kept as it stands, with its identifier and whatever was written on
124
+ * it (`connectVersions`).
125
125
  * @category Collation and alignment
126
126
  */
127
- export const unchecked = 'unchecked';
128
- /**
129
- * The version's statement of what the unchecked motivation means, to stand in its `motivations`.
130
- * @category Collation and alignment
131
- */
132
- export const uncheckedMotivation = {
133
- type: 'motivation',
134
- id: unchecked,
135
- note: 'Made by collating the two texts and not yet read by an editor.'
136
- };
137
- /**
138
- * Whether the edit is one a collation writes by itself: a bare
139
- * insertion or a bare deletion saying nothing further, or a recoding
140
- * that exchanges two systems' spellings for one command.
141
- *
142
- * Collating again rewrites these and keeps the rest. An edit that says
143
- * what the change is, or why it was made, or what it rests on, is an
144
- * editor's reading of the difference between two texts, and collating
145
- * the two again is no reason to discard it.
146
- *
147
- * Such an exchange counts as the collation's own even where an editor
148
- * added a motivation to it, because it is derived rather than read:
149
- * freezing it would leave the transfers, where nearly every edit is
150
- * one, unable to be collated again at all. What an editor wrote on it
151
- * is not lost by that, since `connectVersions` keeps an exchange it
152
- * draws a second time over the very same symbols.
153
- *
154
- * A recoding that only inserts or only deletes is the editor's: a
155
- * collation types no one-sided edit, so one typed so is a reading of
156
- * the transfer that collating again must not undo.
157
- *
158
- * An edit a program proposed and typed is no reading of the editor's
159
- * either, as long as nobody has motivated it: it says so by belonging
160
- * to the proposition set the program's run concluded (`adoptEdits`).
161
- * @category Collation and alignment
162
- */
163
- export const isCollationsOwn = (edit) => (edit.editType === drawnByCollation && !!edit.insert?.length && !!edit.delete?.length)
164
- || ((edit.editType === undefined || propositionSetOf(edit) !== undefined)
165
- && (edit.motivation === undefined || edit.motivation === unchecked)
166
- && !isHeldForItself(edit));
127
+ export const isCollationsOwn = (edit) => edit['@annotation'] === undefined
128
+ && (edit.motivation === undefined || edit.motivation === musicalChange || edit.motivation === technicalChange);
@@ -1,9 +1,7 @@
1
1
  import { Edition } from "../model/Edition.js";
2
- import { PropositionSet } from "../model/Assumption.js";
3
2
  import { Version } from "../model/Version.js";
4
3
  import { EditionOp } from "../ops/draft.js";
5
4
  import { StemmaProposal, Unpinned } from "./proposal.js";
6
- import { Run } from "./record.js";
7
5
  /**
8
6
  * What adopting a proposal makes of the stemma: the versions it puts in
9
7
  * place of the edition's, and what of the edition it leaves out.
@@ -18,11 +16,11 @@ export interface Adoption {
18
16
  /** The edits the editor wrote that the adoption leaves out, and why. */
19
17
  dropped: Unpinned[];
20
18
  /**
21
- * The record of the run: the proposition set the derivations and
22
- * edits the proposal concluded belong to, held under one belief
23
- * whose reason is the inference the program drew.
19
+ * The published rules the proposal follows, as far as its costs and
20
+ * settings carry them out, which the edition's creation comes to
21
+ * name among its principles.
24
22
  */
25
- propositionSet: PropositionSet;
23
+ principles: string[];
26
24
  }
27
25
  /**
28
26
  * What adopting the proposal makes of the edition's stemma, worked out
@@ -36,35 +34,33 @@ export interface Adoption {
36
34
  * the edits the editor wrote that the proposal keeps, as they are, and
37
35
  * one edit for each further reading it inserts or deletes, or for each
38
36
  * recoding. An edit the version stated before over the very same
39
- * readings is taken over with its id and what was written on it; the
40
- * others are new and marked unchecked, as a collation marks its own.
37
+ * readings is taken over with its id and what was written on it, the
38
+ * kind of change it states among it, which the editor may have said
39
+ * otherwise than the program read it; the others are new. An edit that
40
+ * states no kind has the motivation the shape of the change calls for
41
+ * (`motivationProposedFor`): the generic musical one, whose reason is
42
+ * left to the editor, or the technical one, which a recoding always has.
43
+ * The edits of the root state its text, and no kind of change.
41
44
  *
42
45
  * A reading that stands for a symbol of the edition keeps that symbol's
43
46
  * id and whatever it states beside its carriers. A version of the edition
44
47
  * nothing stands for goes, unless it states no edits and derives from a
45
48
  * version that stays, as a version known only from a recording does.
46
49
  *
47
- * What the program concluded is recorded as one proposition set, since
48
- * the search scores whole stemmata and concludes the whole: each
49
- * derivation it states and each edit it makes belongs to the set, which
50
- * holds them under one belief, likely unless the run says otherwise,
51
- * concluded by an inference that names this library and its release,
52
- * the copies it collated, the rules it followed and, in its note, what
53
- * it was computed with. A derivation or an edit that is the editor's,
54
- * held under a belief of its own or saying more than a program says,
55
- * stays the editor's and outside the set. The run gives who ran the
56
- * program and when (`Run`).
50
+ * What the program concluded is stated plainly, as an edition states its
51
+ * stemma. A derivation the editor holds under a belief of their own
52
+ * stays as it is, and so does every edit that says why it was made.
57
53
  * @category Filiation
58
54
  */
59
- export declare const adoptionOf: (edition: Edition, proposal: StemmaProposal, run?: Run) => Adoption;
55
+ export declare const adoptionOf: (edition: Edition, proposal: StemmaProposal) => Adoption;
60
56
  /**
61
57
  * Puts the proposed stemma in place of the edition's, as `adoptionOf`
62
- * describes, with the record of the run, and takes back a copy's
63
- * statement that it carries a version that goes. The record of an
64
- * earlier run whose conclusions are all replaced goes with them, and
65
- * one of the very same run, by the same release on the same copies with
66
- * the same result, stays as it was first recorded. One operation, and
67
- * so one undo step.
58
+ * describes, and takes back a copy's statement that it carries a
59
+ * version that goes. The edition's creation comes to name the rules the
60
+ * proposal follows among its principles, in place of those of the
61
+ * stemma it no longer follows, and this release of the library among its
62
+ * software, which finds the stemma again with the defaults it has. One
63
+ * operation, and so one undo step.
68
64
  * @category Operations
69
65
  */
70
- export declare const adoptStemma: (proposal: StemmaProposal, run?: Run) => EditionOp;
66
+ export declare const adoptStemma: (proposal: StemmaProposal) => EditionOp;
@@ -1,14 +1,16 @@
1
1
  /** Taking a proposed stemma into the edition, as one operation. */
2
2
  import { v5 } from "uuid";
3
- import { assignReference, idOf, isHeldForItself } from "../model/Assumption.js";
3
+ import { musicalChange, technicalChange } from "../model/Edit.js";
4
+ import { isCollationsOwn } from "../collation/Collation.js";
5
+ import { assignReference, idOf } from "../model/Assumption.js";
4
6
  import { derivesFrom, editsOf, insertedBy, principalDerivationOf } from "../model/Version.js";
5
7
  import { systemOf } from "../systems/TrackerBar.js";
6
- import { isCollationsOwn, unchecked } from "../collation/Collation.js";
7
8
  import { versionIn } from "../lookup/lookup.js";
8
- import { declaring, reading } from "../ops/draft.js";
9
+ import { reading } from "../ops/draft.js";
9
10
  import { dropStatements } from "../ops/copies.js";
10
11
  import { barNamed } from "./systems.js";
11
- import { inMillimetres, keptSets, memberOf, recorded, stemmaRules } from "./record.js";
12
+ import { madeWith, stemmaRules } from "./record.js";
13
+ import { motivationOfChanges, proposedMotivations } from "./edits.js";
12
14
  /** The namespace the names of adopted versions and edits are minted in, so that adopting again gives them again. */
13
15
  const ADOPTED = '6f1d2c8e-93a4-4f0b-b8a1-2f4c5d7e9a31';
14
16
  /** The rules the proposal followed, as far as its costs and settings carry them out. */
@@ -22,35 +24,27 @@ const rulesOf = (proposal) => [
22
24
  stemmaRules.refinement,
23
25
  stemmaRules.noContamination
24
26
  ];
25
- /** A number as a note gives it: to two places at most. */
26
- const amount = (value) => String(Math.round(value * 100) / 100);
27
- /**
28
- * What the run of `proposeStemma` was, and what it was computed with, in
29
- * words, so that it can be run again: the copies by siglum, the costs,
30
- * the settings, and the margin by which the copies decide each grouping.
31
- */
32
- const noteOn = (edition, proposal) => {
33
- const siglumOf = new Map(edition.copies.map(copy => [copy.id, copy.siglum ?? copy.id]));
34
- const named = (copies) => copies.map(copy => siglumOf.get(copy) ?? copy).join(', ');
35
- const { costs } = proposal;
36
- const supports = proposal.supports.map(support => support.ancestor
37
- ? `${named(support.ancestor)} above ${named(support.copies.filter(copy => !support.ancestor.includes(copy)))} by ${amount(support.margin)}`
38
- : `${named(support.copies)} together by ${amount(support.margin)}`);
39
- return [
40
- `The stemma ${proposal.notation}, the best of ${proposal.scored} that ${named(proposal.copies)} admit, at a cost of ${amount(proposal.cost)}.`,
41
- `A reading arising costs ${amount(costs.gain)}, a reading lost 1, a redundancy lost ${amount(costs.redundantLoss)}, a reading lost on the way into another system ${amount(costs.transferLoss)}, a copy lacking a reading of its version ${amount(costs.copyDeviation)}, and a lost state ${amount(costs.lostState)}.`,
42
- `Copies of one system differing in ${proposal.sameText < 0 ? 'any reading' : `no more than ${proposal.sameText} readings`} are taken for one text; a reading of the same kind within ${inMillimetres(proposal.movedWithin)} on the other side of the root for a moved one; and ${inMillimetres(proposal.margin)} beyond a copy's text or a tear for what the copy cannot show.`,
43
- ...(supports.length > 0 ? [`The copies decide its groupings by: ${supports.join('; ')}.`] : [])
44
- ].join(' ');
45
- };
46
- /** The set the run of `proposeStemma` concluded, held with the belief the run gives it. */
47
- const stemmaSetOf = (edition, proposal, run) => recorded('stemma', [proposal.copies, proposal.notation, proposal.costs, proposal.sameText, proposal.movedWithin, proposal.margin], { note: noteOn(edition, proposal), premises: [], used: [...proposal.copies], applies: rulesOf(proposal) }, run);
48
27
  const exchangeKey = (inserted, deleted) => `${[...inserted].sort().join(',')}/${[...deleted].sort().join(',')}`;
49
28
  /** The carriers of the reading, keeping what the edition states of those it held already. */
50
29
  const carriersOf = (reading, stated) => {
51
30
  const known = new Map(stated.map(carrier => [idOf(carrier), carrier]));
52
31
  return reading.carriers.map(id => known.get(id) ?? assignReference(id));
53
32
  };
33
+ /** The version with its edits stating its text: no kind of change, though any reason the editor gave. */
34
+ const withoutKinds = (version) => {
35
+ const kindOnly = (edit) => edit.motivation === musicalChange || edit.motivation === technicalChange;
36
+ if (!editsOf(version).some(kindOnly))
37
+ return version;
38
+ return {
39
+ ...version,
40
+ edits: editsOf(version).map(edit => {
41
+ if (!kindOnly(edit))
42
+ return edit;
43
+ const { motivation: _kind, ...text } = edit;
44
+ return text;
45
+ })
46
+ };
47
+ };
54
48
  /**
55
49
  * What adopting the proposal makes of the edition's stemma, worked out
56
50
  * without changing it, so that it can be looked at first.
@@ -63,28 +57,26 @@ const carriersOf = (reading, stated) => {
63
57
  * the edits the editor wrote that the proposal keeps, as they are, and
64
58
  * one edit for each further reading it inserts or deletes, or for each
65
59
  * recoding. An edit the version stated before over the very same
66
- * readings is taken over with its id and what was written on it; the
67
- * others are new and marked unchecked, as a collation marks its own.
60
+ * readings is taken over with its id and what was written on it, the
61
+ * kind of change it states among it, which the editor may have said
62
+ * otherwise than the program read it; the others are new. An edit that
63
+ * states no kind has the motivation the shape of the change calls for
64
+ * (`motivationProposedFor`): the generic musical one, whose reason is
65
+ * left to the editor, or the technical one, which a recoding always has.
66
+ * The edits of the root state its text, and no kind of change.
68
67
  *
69
68
  * A reading that stands for a symbol of the edition keeps that symbol's
70
69
  * id and whatever it states beside its carriers. A version of the edition
71
70
  * nothing stands for goes, unless it states no edits and derives from a
72
71
  * version that stays, as a version known only from a recording does.
73
72
  *
74
- * What the program concluded is recorded as one proposition set, since
75
- * the search scores whole stemmata and concludes the whole: each
76
- * derivation it states and each edit it makes belongs to the set, which
77
- * holds them under one belief, likely unless the run says otherwise,
78
- * concluded by an inference that names this library and its release,
79
- * the copies it collated, the rules it followed and, in its note, what
80
- * it was computed with. A derivation or an edit that is the editor's,
81
- * held under a belief of its own or saying more than a program says,
82
- * stays the editor's and outside the set. The run gives who ran the
83
- * program and when (`Run`).
73
+ * What the program concluded is stated plainly, as an edition states its
74
+ * stemma. A derivation the editor holds under a belief of their own
75
+ * stays as it is, and so does every edit that says why it was made.
84
76
  * @category Filiation
85
77
  */
86
- export const adoptionOf = (edition, proposal, run = {}) => {
87
- const set = stemmaSetOf(edition, proposal, run);
78
+ export const adoptionOf = (edition, proposal) => {
79
+ const fresh = new Set();
88
80
  const readingOf = new Map(proposal.collation.readings.map(reading => [reading.id, reading]));
89
81
  const symbolIdOf = (reading) => proposal.symbols[reading] ?? reading;
90
82
  const existing = new Map(edition.versions.flatMap(insertedBy).map(symbol => [symbol.id, symbol]));
@@ -159,18 +151,15 @@ export const adoptionOf = (edition, proposal, run = {}) => {
159
151
  const key = exchangeKey(inserted, deleted);
160
152
  const same = statedBefore.get(key);
161
153
  const insert = inserts.map(reading => symbolOf(reading, proposed.id));
162
- if (same) {
163
- const taken = { ...same, ...(insert.length > 0 && { insert }), ...(deleted.length > 0 && { delete: deleted }) };
164
- return isCollationsOwn(same) ? memberOf(set.id, taken) : taken;
165
- }
166
- return memberOf(set.id, {
167
- type: 'edit',
168
- id: v5(`edit|${id}|${key}`, ADOPTED),
169
- ...(recoding && { editType: 'recoding' }),
170
- motivation: unchecked,
171
- ...(insert.length > 0 && { insert }),
172
- ...(deleted.length > 0 && { delete: deleted })
173
- });
154
+ const exchange = { ...(insert.length > 0 && { insert }), ...(deleted.length > 0 && { delete: deleted }) };
155
+ // An edit over the same changes stays, with the kind of change it states, which the editor may have said.
156
+ if (same && (!isCollationsOwn(same) || same.motivation !== undefined))
157
+ return { ...same, ...exchange };
158
+ const made = { ...(same ?? { type: 'edit', id: v5(`edit|${id}|${key}`, ADOPTED) }), ...(recoding && { motivation: technicalChange }), ...exchange };
159
+ // A recoding is technical; the motivation of any other change is read off its shape below.
160
+ if (!recoding)
161
+ fresh.add(made.id);
162
+ return made;
174
163
  };
175
164
  // A recoding an edit of the editor's cuts into is stated reading by reading.
176
165
  const whole = proposed.recodings.filter(recoding => ![...recoding.by.map(reading => symbolIdAt(reading, proposed.id)), ...recoding.replaced.map(reading => inheritedAt(reading, proposed.id))]
@@ -182,13 +171,11 @@ export const adoptionOf = (edition, proposal, run = {}) => {
182
171
  ...proposed.inserts.filter(reading => !recoded.has(reading)).map(reading => made([reading], [], false)),
183
172
  ...proposed.deletes.filter(reading => !recoded.has(reading)).map(reading => made([], [reading], false))
184
173
  ].filter((edit) => edit !== undefined);
185
- // The derivation is the set's, unless the editor holds it for reasons of their own.
174
+ // A derivation stated before is kept with what it states, a belief of the editor's among it.
186
175
  const parent = proposed.parent ? ids.get(proposed.parent) : undefined;
187
176
  const principal = before && principalDerivationOf(before);
188
177
  const same = principal && idOf(principal) === parent ? principal : undefined;
189
- const derivation = parent === undefined
190
- ? undefined
191
- : same && isHeldForItself(same) ? same : memberOf(set.id, same ?? assignReference(parent));
178
+ const derivation = parent === undefined ? undefined : same ?? assignReference(parent);
192
179
  const hypotheses = (before?.basedOn ?? [])
193
180
  .filter(other => other !== principal && staying.has(idOf(other)) && idOf(other) !== parent);
194
181
  const basedOn = [...(derivation ? [derivation] : []), ...hypotheses];
@@ -201,8 +188,7 @@ export const adoptionOf = (edition, proposal, run = {}) => {
201
188
  id,
202
189
  system,
203
190
  ...(basedOn.length > 0 && { basedOn }),
204
- edits,
205
- motivations: declaring(before?.motivations ?? [], edits)
191
+ edits
206
192
  };
207
193
  });
208
194
  // A hypothesis may not name a version that now derives from the one stating it.
@@ -222,28 +208,40 @@ export const adoptionOf = (edition, proposal, run = {}) => {
222
208
  // The edition's order where a version stays, the new ones after it.
223
209
  const rank = new Map(edition.versions.map((version, index) => [version.id, index]));
224
210
  const ordered = [...pruned, ...statedOnly].sort((a, b) => (rank.get(a.id) ?? Infinity) - (rank.get(b.id) ?? Infinity));
225
- return { versions: ordered, ids: Object.fromEntries(ids), removed, dropped: proposal.unpinned, propositionSet: set };
211
+ // A new edit's motivation follows from the shape of the change, read in the stemma adopted.
212
+ const adopted = { ...edition, versions: ordered };
213
+ const motivated = ordered.map(version => {
214
+ if (!principalDerivationOf(version))
215
+ return withoutKinds(version);
216
+ if (!editsOf(version).some(edit => fresh.has(edit.id)))
217
+ return version;
218
+ const motivations = proposedMotivations(adopted, version.id);
219
+ return {
220
+ ...version,
221
+ edits: editsOf(version).map(edit => fresh.has(edit.id)
222
+ ? { ...edit, motivation: motivationOfChanges(motivations, [...(edit.insert ?? []).map(symbol => symbol.id), ...(edit.delete ?? [])]) }
223
+ : edit)
224
+ };
225
+ });
226
+ return { versions: motivated, ids: Object.fromEntries(ids), removed, dropped: proposal.unpinned, principles: rulesOf(proposal) };
226
227
  };
227
228
  /**
228
229
  * Puts the proposed stemma in place of the edition's, as `adoptionOf`
229
- * describes, with the record of the run, and takes back a copy's
230
- * statement that it carries a version that goes. The record of an
231
- * earlier run whose conclusions are all replaced goes with them, and
232
- * one of the very same run, by the same release on the same copies with
233
- * the same result, stays as it was first recorded. One operation, and
234
- * so one undo step.
230
+ * describes, and takes back a copy's statement that it carries a
231
+ * version that goes. The edition's creation comes to name the rules the
232
+ * proposal follows among its principles, in place of those of the
233
+ * stemma it no longer follows, and this release of the library among its
234
+ * software, which finds the stemma again with the defaults it has. One
235
+ * operation, and so one undo step.
235
236
  * @category Operations
236
237
  */
237
- export const adoptStemma = (proposal, run = {}) => reading(edition => {
238
- const { versions, removed, propositionSet } = adoptionOf(edition, proposal, run);
238
+ export const adoptStemma = (proposal) => reading(edition => {
239
+ const { versions, removed, principles } = adoptionOf(edition, proposal);
239
240
  const gone = new Set(removed);
240
- const sets = keptSets({ ...edition, versions }, [propositionSet]);
241
+ const creation = madeWith(edition.creation, principles, Object.values(stemmaRules));
241
242
  return draft => {
242
243
  draft.versions = versions;
243
244
  draft.copies.forEach(copy => dropStatements(copy, statement => gone.has(idOf(statement))));
244
- if (sets)
245
- draft.propositionSets = sets;
246
- else
247
- delete draft.propositionSets;
245
+ draft.creation = creation;
248
246
  };
249
247
  });