linked-rolls 0.74.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.
Files changed (42) hide show
  1. package/README.md +62 -26
  2. package/lib/collation/Collation.d.ts +12 -42
  3. package/lib/collation/Collation.js +15 -48
  4. package/lib/filiation/adoption.d.ts +23 -4
  5. package/lib/filiation/adoption.js +81 -23
  6. package/lib/filiation/edits.d.ts +35 -19
  7. package/lib/filiation/edits.js +68 -34
  8. package/lib/filiation/index.d.ts +2 -1
  9. package/lib/filiation/index.js +2 -1
  10. package/lib/filiation/record.d.ts +49 -0
  11. package/lib/filiation/record.js +49 -0
  12. package/lib/index.d.ts +1 -0
  13. package/lib/index.js +1 -0
  14. package/lib/io/asJsonLd.js +5 -1
  15. package/lib/io/importJsonLd.js +42 -4
  16. package/lib/io/migrate.d.ts +7 -0
  17. package/lib/io/migrate.js +196 -2
  18. package/lib/model/Agent.d.ts +3 -1
  19. package/lib/model/Assumption.d.ts +8 -1
  20. package/lib/model/Assumption.js +2 -1
  21. package/lib/model/Edit.d.ts +76 -17
  22. package/lib/model/Edit.js +52 -3
  23. package/lib/model/Edition.d.ts +32 -3
  24. package/lib/model/RollCopy.js +1 -1
  25. package/lib/model/Version.d.ts +3 -13
  26. package/lib/model/Version.js +7 -2
  27. package/lib/ops/collation.d.ts +8 -4
  28. package/lib/ops/collation.js +40 -23
  29. package/lib/ops/copies.js +1 -2
  30. package/lib/ops/draft.d.ts +1 -9
  31. package/lib/ops/draft.js +0 -12
  32. package/lib/ops/edits.d.ts +20 -3
  33. package/lib/ops/edits.js +36 -15
  34. package/lib/ops/index.d.ts +1 -1
  35. package/lib/ops/index.js +1 -1
  36. package/lib/ops/versions.d.ts +3 -2
  37. package/lib/ops/versions.js +4 -5
  38. package/lib/schema.json +60 -64
  39. package/lib/software.d.ts +12 -0
  40. package/lib/software.js +10 -0
  41. package/lib/spec/context.json +8 -5
  42. package/package.json +1 -1
package/README.md CHANGED
@@ -132,6 +132,22 @@ 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 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.
144
+
145
+ ```ts
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' }]
149
+ ```
150
+
135
151
  The costs are those with which the stemma of WM 225 comes out of its
136
152
  six copies. They were found by searching for such values, so they show
137
153
  that the stemma is consistent with these principles, not that the
@@ -139,24 +155,26 @@ principles confirm it.
139
155
 
140
156
  ## Proposing the edits
141
157
 
142
- What a version inserts and deletes can be grouped into edits and typed
143
- by their shape alone. `proposeEdits` reads a version's changes line by
144
- line, a latched function, a held one or a pitch, and never across a
145
- command both texts keep, so that an edit stays at one place. A function
146
- turned on and off again is one edit, typed an additional accent where
147
- the bar spells an accent so; a perforation put elsewhere is one move
148
- with the one it replaces, typed a shift, or shortened or prolonged
149
- where it begins about where the other did; overlapping notes are one
150
- edit, a note struck again or two tied; a held command standing for a
151
- latched pair is one recoding; a command the text makes no use of is a
152
- redundancy added or removed; cancels punched across lines at one place
153
- are one row. `adoptEdits` puts the proposal in place of the edits a
154
- collation wrote and nobody has motivated, and keeps the rest.
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.
155
173
 
156
174
  ```ts
157
175
  import { adoptEdits, adoptStemma, proposeEdits, proposeStemma } from 'linked-rolls'
158
176
 
159
- proposeEdits(edition, versionId) // [{ insert, delete, editType, grouping }, …]
177
+ proposeEdits(edition, versionId) // [{ insert, delete, shape, grouping }, …]
160
178
 
161
179
  // The stemma and the edits of its versions, as one undo step
162
180
  const adopted = produce(edition, draft => {
@@ -165,12 +183,26 @@ const adopted = produce(edition, draft => {
165
183
  })
166
184
  ```
167
185
 
168
- Why an edit was made is not read off its shape, and neither is whether
169
- a shift corrects an error: what is left to the editor is the
170
- motivation. On WM 225 the proposal reproduces 370 of the 414 edits the
171
- editor wrote, from what each version changes, and all of their types
172
- but the shifts the editor took for corrected errors. From the copies
173
- 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.
174
206
 
175
207
  ## Format
176
208
 
@@ -182,7 +214,10 @@ https://pfefferniels.github.io/linked-rolls/, to which the w3id.org
182
214
  identifiers redirect.
183
215
 
184
216
  Beliefs are written as JSON-LD-star annotations (`@annotation`), so you
185
- need a processor that supports JSON-LD-star to read them.
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.
186
221
 
187
222
  ## Development
188
223
 
@@ -199,11 +234,12 @@ TypeScript 7 no longer has the compiler API it reads the source with.
199
234
 
200
235
  ## Releasing
201
236
 
202
- Raise the version in `package.json` and in `CITATION.cff`, there with
203
- the day as `date-released`, commit, and push a tag `v<version>`. The
204
- `publish.yml` workflow builds, tests and publishes to npm, then makes a
205
- GitHub release of the tag, which Zenodo archives with a DOI. A test fails
206
- while the two versions differ.
237
+ Raise the version in `package.json`, in `CITATION.cff`, there with the
238
+ day as `date-released`, and in `src/software.ts`, which names the release
239
+ in the edition's creation when a proposal is adopted. Then commit, and push a tag
240
+ `v<version>`. The `publish.yml` workflow builds, tests and publishes to
241
+ npm, then makes a GitHub release of the tag, which Zenodo archives with
242
+ a DOI. A test fails while the versions differ.
207
243
 
208
244
  ## About
209
245
 
@@ -120,50 +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.
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`).
167
137
  * @category Collation and alignment
168
138
  */
169
139
  export declare const isCollationsOwn: (edit: Readonly<Edit>) => boolean;
@@ -1,3 +1,4 @@
1
+ import { musicalChange, technicalChange } from "../model/Edit.js";
1
2
  import { keyOf } from "../systems/TrackerBar.js";
2
3
  import { distance, mm } from "../model/Quantity.js";
3
4
  import { partitionPoint } from "../shared/sorted.js";
@@ -106,56 +107,22 @@ export const collationsOf = (own, inherited, locate, tolerance = defaultCollatio
106
107
  .sort((a, b) => a.index - b.index)
107
108
  .map(({ symbol: counterpart }) => ({ symbol, counterpart })));
108
109
  };
109
- /** The edit type a collation draws by itself, from the two systems' vocabularies rather than off the paper. */
110
- const drawnByCollation = 'recoding';
111
110
  /**
112
- * The motivation a collation writes on what it makes, saying that
113
- * 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.
114
117
  *
115
- * A collation produces its edits by a rule, and an edition that
116
- * publishes them unmarked would present a machine's output as an
117
- * editor's reading. Stating that they are unchecked is the honest
118
- * middle: the readings are there to be seen and argued with, and a
119
- * 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.
120
121
  *
121
- * It does not make an edit the editor's. `isCollationsOwn` passes over
122
- * this motivation exactly, so a collation rewrites what it marked
123
- * 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`).
124
125
  * @category Collation and alignment
125
126
  */
126
- export const unchecked = 'unchecked';
127
- /**
128
- * The version's statement of what the unchecked motivation means, to stand in its `motivations`.
129
- * @category Collation and alignment
130
- */
131
- export const uncheckedMotivation = {
132
- type: 'motivation',
133
- id: unchecked,
134
- note: 'Made by collating the two texts and not yet read by an editor.'
135
- };
136
- /**
137
- * Whether the edit is one a collation writes by itself: a bare
138
- * insertion or a bare deletion saying nothing further, or a recoding
139
- * that exchanges two systems' spellings for one command.
140
- *
141
- * Collating again rewrites these and keeps the rest. An edit that says
142
- * what the change is, or why it was made, or what it rests on, is an
143
- * editor's reading of the difference between two texts, and collating
144
- * the two again is no reason to discard it.
145
- *
146
- * Such an exchange counts as the collation's own even where an editor
147
- * added a motivation to it, because it is derived rather than read:
148
- * freezing it would leave the transfers, where nearly every edit is
149
- * one, unable to be collated again at all. What an editor wrote on it
150
- * is not lost by that, since `connectVersions` keeps an exchange it
151
- * draws a second time over the very same symbols.
152
- *
153
- * A recoding that only inserts or only deletes is the editor's: a
154
- * collation types no one-sided edit, so one typed so is a reading of
155
- * the transfer that collating again must not undo.
156
- * @category Collation and alignment
157
- */
158
- export const isCollationsOwn = (edit) => (edit.editType === drawnByCollation && !!edit.insert?.length && !!edit.delete?.length)
159
- || (edit.editType === undefined
160
- && (edit.motivation === undefined || edit.motivation === unchecked)
161
- && edit['@annotation'] === undefined);
127
+ export const isCollationsOwn = (edit) => edit['@annotation'] === undefined
128
+ && (edit.motivation === undefined || edit.motivation === musicalChange || edit.motivation === technicalChange);
@@ -15,6 +15,12 @@ export interface Adoption {
15
15
  removed: string[];
16
16
  /** The edits the editor wrote that the adoption leaves out, and why. */
17
17
  dropped: Unpinned[];
18
+ /**
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.
22
+ */
23
+ principles: string[];
18
24
  }
19
25
  /**
20
26
  * What adopting the proposal makes of the edition's stemma, worked out
@@ -28,20 +34,33 @@ export interface Adoption {
28
34
  * the edits the editor wrote that the proposal keeps, as they are, and
29
35
  * one edit for each further reading it inserts or deletes, or for each
30
36
  * recoding. An edit the version stated before over the very same
31
- * readings is taken over with its id and what was written on it; the
32
- * 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.
33
44
  *
34
45
  * A reading that stands for a symbol of the edition keeps that symbol's
35
46
  * id and whatever it states beside its carriers. A version of the edition
36
47
  * nothing stands for goes, unless it states no edits and derives from a
37
48
  * version that stays, as a version known only from a recording does.
49
+ *
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.
38
53
  * @category Filiation
39
54
  */
40
55
  export declare const adoptionOf: (edition: Edition, proposal: StemmaProposal) => Adoption;
41
56
  /**
42
57
  * Puts the proposed stemma in place of the edition's, as `adoptionOf`
43
- * describes, and takes back a copy's statement that it carries a version
44
- * that goes. One operation, and 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.
45
64
  * @category Operations
46
65
  */
47
66
  export declare const adoptStemma: (proposal: StemmaProposal) => EditionOp;
@@ -1,21 +1,50 @@
1
1
  /** Taking a proposed stemma into the edition, as one operation. */
2
2
  import { v5 } from "uuid";
3
+ import { musicalChange, technicalChange } from "../model/Edit.js";
4
+ import { isCollationsOwn } from "../collation/Collation.js";
3
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 { 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";
12
+ import { madeWith, stemmaRules } from "./record.js";
13
+ import { motivationOfChanges, proposedMotivations } from "./edits.js";
11
14
  /** The namespace the names of adopted versions and edits are minted in, so that adopting again gives them again. */
12
15
  const ADOPTED = '6f1d2c8e-93a4-4f0b-b8a1-2f4c5d7e9a31';
16
+ /** The rules the proposal followed, as far as its costs and settings carry them out. */
17
+ const rulesOf = (proposal) => [
18
+ ...(proposal.costs.gain > 1 ? [stemmaRules.sharedAddition] : []),
19
+ ...(proposal.costs.redundantLoss < 1 ? [stemmaRules.independentCleanup] : []),
20
+ ...(proposal.costs.transferLoss < 1 ? [stemmaRules.recutLoss] : []),
21
+ ...(Number.isFinite(proposal.costs.copyDeviation) ? [stemmaRules.copyDeviation] : []),
22
+ ...(proposal.costs.lostState > 0 ? [stemmaRules.lostStates] : []),
23
+ ...(proposal.sameText >= 0 ? [stemmaRules.oneText] : []),
24
+ stemmaRules.refinement,
25
+ stemmaRules.noContamination
26
+ ];
13
27
  const exchangeKey = (inserted, deleted) => `${[...inserted].sort().join(',')}/${[...deleted].sort().join(',')}`;
14
28
  /** The carriers of the reading, keeping what the edition states of those it held already. */
15
29
  const carriersOf = (reading, stated) => {
16
30
  const known = new Map(stated.map(carrier => [idOf(carrier), carrier]));
17
31
  return reading.carriers.map(id => known.get(id) ?? assignReference(id));
18
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
+ };
19
48
  /**
20
49
  * What adopting the proposal makes of the edition's stemma, worked out
21
50
  * without changing it, so that it can be looked at first.
@@ -28,16 +57,26 @@ const carriersOf = (reading, stated) => {
28
57
  * the edits the editor wrote that the proposal keeps, as they are, and
29
58
  * one edit for each further reading it inserts or deletes, or for each
30
59
  * recoding. An edit the version stated before over the very same
31
- * readings is taken over with its id and what was written on it; the
32
- * 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.
33
67
  *
34
68
  * A reading that stands for a symbol of the edition keeps that symbol's
35
69
  * id and whatever it states beside its carriers. A version of the edition
36
70
  * nothing stands for goes, unless it states no edits and derives from a
37
71
  * version that stays, as a version known only from a recording does.
72
+ *
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.
38
76
  * @category Filiation
39
77
  */
40
78
  export const adoptionOf = (edition, proposal) => {
79
+ const fresh = new Set();
41
80
  const readingOf = new Map(proposal.collation.readings.map(reading => [reading.id, reading]));
42
81
  const symbolIdOf = (reading) => proposal.symbols[reading] ?? reading;
43
82
  const existing = new Map(edition.versions.flatMap(insertedBy).map(symbol => [symbol.id, symbol]));
@@ -112,16 +151,15 @@ export const adoptionOf = (edition, proposal) => {
112
151
  const key = exchangeKey(inserted, deleted);
113
152
  const same = statedBefore.get(key);
114
153
  const insert = inserts.map(reading => symbolOf(reading, proposed.id));
115
- if (same)
116
- return { ...same, ...(insert.length > 0 && { insert }), ...(deleted.length > 0 && { delete: deleted }) };
117
- return {
118
- type: 'edit',
119
- id: v5(`edit|${id}|${key}`, ADOPTED),
120
- ...(recoding && { editType: 'recoding' }),
121
- motivation: unchecked,
122
- ...(insert.length > 0 && { insert }),
123
- ...(deleted.length > 0 && { delete: deleted })
124
- };
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;
125
163
  };
126
164
  // A recoding an edit of the editor's cuts into is stated reading by reading.
127
165
  const whole = proposed.recodings.filter(recoding => ![...recoding.by.map(reading => symbolIdAt(reading, proposed.id)), ...recoding.replaced.map(reading => inheritedAt(reading, proposed.id))]
@@ -133,11 +171,11 @@ export const adoptionOf = (edition, proposal) => {
133
171
  ...proposed.inserts.filter(reading => !recoded.has(reading)).map(reading => made([reading], [], false)),
134
172
  ...proposed.deletes.filter(reading => !recoded.has(reading)).map(reading => made([], [reading], false))
135
173
  ].filter((edit) => edit !== undefined);
174
+ // A derivation stated before is kept with what it states, a belief of the editor's among it.
136
175
  const parent = proposed.parent ? ids.get(proposed.parent) : undefined;
137
176
  const principal = before && principalDerivationOf(before);
138
- const derivation = parent === undefined
139
- ? undefined
140
- : principal && idOf(principal) === parent ? principal : assignReference(parent);
177
+ const same = principal && idOf(principal) === parent ? principal : undefined;
178
+ const derivation = parent === undefined ? undefined : same ?? assignReference(parent);
141
179
  const hypotheses = (before?.basedOn ?? [])
142
180
  .filter(other => other !== principal && staying.has(idOf(other)) && idOf(other) !== parent);
143
181
  const basedOn = [...(derivation ? [derivation] : []), ...hypotheses];
@@ -150,8 +188,7 @@ export const adoptionOf = (edition, proposal) => {
150
188
  id,
151
189
  system,
152
190
  ...(basedOn.length > 0 && { basedOn }),
153
- edits,
154
- motivations: declaring(before?.motivations ?? [], edits)
191
+ edits
155
192
  };
156
193
  });
157
194
  // A hypothesis may not name a version that now derives from the one stating it.
@@ -171,19 +208,40 @@ export const adoptionOf = (edition, proposal) => {
171
208
  // The edition's order where a version stays, the new ones after it.
172
209
  const rank = new Map(edition.versions.map((version, index) => [version.id, index]));
173
210
  const ordered = [...pruned, ...statedOnly].sort((a, b) => (rank.get(a.id) ?? Infinity) - (rank.get(b.id) ?? Infinity));
174
- return { versions: ordered, ids: Object.fromEntries(ids), removed, dropped: proposal.unpinned };
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) };
175
227
  };
176
228
  /**
177
229
  * Puts the proposed stemma in place of the edition's, as `adoptionOf`
178
- * describes, and takes back a copy's statement that it carries a version
179
- * that goes. One operation, and 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.
180
236
  * @category Operations
181
237
  */
182
238
  export const adoptStemma = (proposal) => reading(edition => {
183
- const { versions, removed } = adoptionOf(edition, proposal);
239
+ const { versions, removed, principles } = adoptionOf(edition, proposal);
184
240
  const gone = new Set(removed);
241
+ const creation = madeWith(edition.creation, principles, Object.values(stemmaRules));
185
242
  return draft => {
186
243
  draft.versions = versions;
187
244
  draft.copies.forEach(copy => dropStatements(copy, statement => gone.has(idOf(statement))));
245
+ draft.creation = creation;
188
246
  };
189
247
  });
@@ -1,5 +1,5 @@
1
1
  import { Edition } from "../model/Edition.js";
2
- import { Edit, EditType } from "../model/Edit.js";
2
+ import { EditShape } from "../model/Edit.js";
3
3
  import { Millimeters } from "../model/Quantity.js";
4
4
  import { EditionOp } from "../ops/draft.js";
5
5
  /**
@@ -69,8 +69,8 @@ export interface ProposedEdit {
69
69
  insert: string[];
70
70
  /** The ids of the symbols it deletes, in the order of their places. */
71
71
  delete: string[];
72
- /** The kind of change, where its shape says. */
73
- editType?: EditType;
72
+ /** The shape of the change, where it takes one the program knows. */
73
+ shape?: EditShape;
74
74
  grouping: EditGrouping;
75
75
  }
76
76
  /**
@@ -94,8 +94,8 @@ export interface ProposedEdit {
94
94
  * punched across three lines or more at one place are one edit, as a
95
95
  * cancel row is.
96
96
  *
97
- * The type is read off the shape: a pair the bar spells an accent with is
98
- * an additional accent, as is a note set off from its chord by a few
97
+ * The shape is read off the group: a pair the bar spells an accent with
98
+ * is an additional accent, as is a note set off from its chord by a few
99
99
  * millimetres where the version puts an accent's On; a perforation put
100
100
  * elsewhere is a shift, or where it begins about where the other did,
101
101
  * shortened or prolonged; a note struck again is shortened, two tied
@@ -110,25 +110,41 @@ export interface ProposedEdit {
110
110
  */
111
111
  export declare const proposeEdits: (edition: Edition, versionId: string, options?: EditOptions) => ProposedEdit[];
112
112
  /**
113
- * Whether the edit is left for a proposal to group again: one a
114
- * collation or an adoption wrote, that nobody has given a motivation.
113
+ * The motivation a program states for each change the version makes,
114
+ * by the id of the symbol it inserts or deletes: the one the shape of
115
+ * the proposed edit it belongs to calls for (`motivationProposedFor`),
116
+ * musical or technical. A version that derives from none makes no
117
+ * change, and its symbols are given none.
115
118
  * @category Filiation
116
119
  */
117
- export declare const isRegroupable: (edit: Readonly<Edit>) => boolean;
120
+ export declare const proposedMotivations: (edition: Edition, versionId: string, options?: EditOptions) => ReadonlyMap<string, string>;
118
121
  /**
119
- * Puts the proposed edits (`proposeEdits`) in place of those of the
120
- * versions that a collation or an adoption wrote and nobody has given a
121
- * motivation, every version but the root where none are named. An edit
122
- * someone motivated, typed beyond what a collation types, or held under a
123
- * belief stays as it is, and what a proposed edit shares with it stands
124
- * symbol by symbol beside it. An edit stated already over the very same
125
- * symbols as a proposed one is kept with its id and what was written on
126
- * it, the proposed type filled in where it states none; the others are
127
- * new, with ids that adopting again gives again, and marked unchecked,
128
- * so that what is left to the editor is to say why.
122
+ * The motivation a program states for an edit making these changes:
123
+ * the generic musical one where any of them is a musical change, or one
124
+ * the proposal does not see, and the technical one where all are
125
+ * technical.
126
+ */
127
+ export declare const motivationOfChanges: (motivations: ReadonlyMap<string, string>, symbolIds: readonly string[]) => string;
128
+ /**
129
+ * Puts the proposed edits (`proposeEdits`) in place of the program's
130
+ * edits of the versions, every version but the root where none are
131
+ * named: those that say nothing of why they were made, or only that they
132
+ * are musical or technical changes (`isCollationsOwn`). An edit with a
133
+ * motivation of the editor's or held under a belief stays as it is, and
134
+ * what a proposed edit shares with it stands symbol by symbol beside it.
135
+ * An edit stated already over the very same symbols as a proposed one
136
+ * is kept with its id and the kind of change it states, which the
137
+ * editor may have said otherwise than the program read it; the others
138
+ * are new, with ids that adopting again gives again. An edit that
139
+ * states no kind has the motivation its shape calls for
140
+ * (`motivationProposedFor`): the generic musical one for a musical
141
+ * change, whose reason is left to the editor, and the technical one for
142
+ * a technical change.
129
143
  *
130
144
  * Applied on the same draft after `adoptStemma`, it proposes the edits of
131
- * the stemma adopted, as one undo step.
145
+ * the stemma adopted, as one undo step. The edition's creation comes to
146
+ * name the rule that keeps an edit at one place among its principles,
147
+ * and this release among its software.
132
148
  * @category Operations
133
149
  */
134
150
  export declare const adoptEdits: (versionIds?: readonly string[], options?: EditOptions) => EditionOp;