@graview/core 0.1.60 → 0.1.62

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 (307) hide show
  1. package/README.md +23 -1
  2. package/dist/app-places.d.ts.map +1 -1
  3. package/dist/app-places.js +2 -2
  4. package/dist/app-places.js.map +1 -1
  5. package/dist/app.d.ts +10 -0
  6. package/dist/app.d.ts.map +1 -1
  7. package/dist/app.js.map +1 -1
  8. package/dist/arrangement.d.ts +2 -0
  9. package/dist/arrangement.d.ts.map +1 -1
  10. package/dist/arrangement.js +14 -0
  11. package/dist/arrangement.js.map +1 -1
  12. package/dist/capabilities.js +1 -1
  13. package/dist/capabilities.js.map +1 -1
  14. package/dist/catalog.d.ts.map +1 -1
  15. package/dist/catalog.js +2 -1
  16. package/dist/catalog.js.map +1 -1
  17. package/dist/cli/check/context.d.ts +5 -0
  18. package/dist/cli/check/context.d.ts.map +1 -1
  19. package/dist/cli/check/context.js.map +1 -1
  20. package/dist/cli/check/face.d.ts +14 -0
  21. package/dist/cli/check/face.d.ts.map +1 -0
  22. package/dist/cli/check/face.js +161 -0
  23. package/dist/cli/check/face.js.map +1 -0
  24. package/dist/cli/check/hidden-reads.d.ts.map +1 -1
  25. package/dist/cli/check/hidden-reads.js +26 -0
  26. package/dist/cli/check/hidden-reads.js.map +1 -1
  27. package/dist/cli/check/index.d.ts +1 -0
  28. package/dist/cli/check/index.d.ts.map +1 -1
  29. package/dist/cli/check/index.js +1 -0
  30. package/dist/cli/check/index.js.map +1 -1
  31. package/dist/cli/check/tidying.d.ts +14 -0
  32. package/dist/cli/check/tidying.d.ts.map +1 -0
  33. package/dist/cli/check/tidying.js +24 -0
  34. package/dist/cli/check/tidying.js.map +1 -0
  35. package/dist/cli/check.d.ts.map +1 -1
  36. package/dist/cli/check.js +4 -4
  37. package/dist/cli/check.js.map +1 -1
  38. package/dist/cli/describe.d.ts.map +1 -1
  39. package/dist/cli/describe.js +14 -1
  40. package/dist/cli/describe.js.map +1 -1
  41. package/dist/cli/docs.d.ts.map +1 -1
  42. package/dist/cli/docs.js +5 -3
  43. package/dist/cli/docs.js.map +1 -1
  44. package/dist/cli/document-schema.d.ts.map +1 -1
  45. package/dist/cli/document-schema.js +8 -2
  46. package/dist/cli/document-schema.js.map +1 -1
  47. package/dist/cli/index.d.ts +2 -1
  48. package/dist/cli/index.d.ts.map +1 -1
  49. package/dist/cli/index.js +15 -4
  50. package/dist/cli/index.js.map +1 -1
  51. package/dist/cli/types.d.ts.map +1 -1
  52. package/dist/cli/types.js +2 -1
  53. package/dist/cli/types.js.map +1 -1
  54. package/dist/compose.d.ts +14 -0
  55. package/dist/compose.d.ts.map +1 -1
  56. package/dist/compose.js +16 -1
  57. package/dist/compose.js.map +1 -1
  58. package/dist/css/sanitize.d.ts +125 -0
  59. package/dist/css/sanitize.d.ts.map +1 -0
  60. package/dist/css/sanitize.js +989 -0
  61. package/dist/css/sanitize.js.map +1 -0
  62. package/dist/css/tables.d.ts +55 -0
  63. package/dist/css/tables.d.ts.map +1 -0
  64. package/dist/css/tables.js +137 -0
  65. package/dist/css/tables.js.map +1 -0
  66. package/dist/css.d.ts +20 -0
  67. package/dist/css.d.ts.map +1 -0
  68. package/dist/css.js +19 -0
  69. package/dist/css.js.map +1 -0
  70. package/dist/days.d.ts +15 -0
  71. package/dist/days.d.ts.map +1 -1
  72. package/dist/days.js +21 -0
  73. package/dist/days.js.map +1 -1
  74. package/dist/describe.d.ts +1 -1
  75. package/dist/describe.d.ts.map +1 -1
  76. package/dist/document/act-findings.d.ts.map +1 -1
  77. package/dist/document/act-findings.js +80 -1
  78. package/dist/document/act-findings.js.map +1 -1
  79. package/dist/document/blocks.d.ts.map +1 -1
  80. package/dist/document/blocks.js +2 -1
  81. package/dist/document/blocks.js.map +1 -1
  82. package/dist/document/brand-check.d.ts +15 -1
  83. package/dist/document/brand-check.d.ts.map +1 -1
  84. package/dist/document/brand-check.js +80 -0
  85. package/dist/document/brand-check.js.map +1 -1
  86. package/dist/document/brand.d.ts.map +1 -1
  87. package/dist/document/brand.js +2 -0
  88. package/dist/document/brand.js.map +1 -1
  89. package/dist/document/compile-checked.d.ts.map +1 -1
  90. package/dist/document/compile-checked.js +4 -3
  91. package/dist/document/compile-checked.js.map +1 -1
  92. package/dist/document/compile.d.ts.map +1 -1
  93. package/dist/document/compile.js +54 -9
  94. package/dist/document/compile.js.map +1 -1
  95. package/dist/document/compiled.d.ts +18 -3
  96. package/dist/document/compiled.d.ts.map +1 -1
  97. package/dist/document/compiled.js +91 -10
  98. package/dist/document/compiled.js.map +1 -1
  99. package/dist/document/connection-findings.d.ts.map +1 -1
  100. package/dist/document/connection-findings.js +219 -19
  101. package/dist/document/connection-findings.js.map +1 -1
  102. package/dist/document/connection-spec.d.ts +7 -0
  103. package/dist/document/connection-spec.d.ts.map +1 -1
  104. package/dist/document/connection-spec.js +26 -2
  105. package/dist/document/connection-spec.js.map +1 -1
  106. package/dist/document/covers.d.ts +48 -0
  107. package/dist/document/covers.d.ts.map +1 -0
  108. package/dist/document/covers.js +90 -0
  109. package/dist/document/covers.js.map +1 -0
  110. package/dist/document/describe-place.d.ts +3 -0
  111. package/dist/document/describe-place.d.ts.map +1 -1
  112. package/dist/document/describe-place.js +11 -0
  113. package/dist/document/describe-place.js.map +1 -1
  114. package/dist/document/described.d.ts +28 -0
  115. package/dist/document/described.d.ts.map +1 -1
  116. package/dist/document/diff.js +10 -1
  117. package/dist/document/diff.js.map +1 -1
  118. package/dist/document/edit.d.ts +1 -1
  119. package/dist/document/edit.d.ts.map +1 -1
  120. package/dist/document/edit.js +186 -18
  121. package/dist/document/edit.js.map +1 -1
  122. package/dist/document/expr/evaluate.d.ts +10 -1
  123. package/dist/document/expr/evaluate.d.ts.map +1 -1
  124. package/dist/document/expr/evaluate.js +110 -2
  125. package/dist/document/expr/evaluate.js.map +1 -1
  126. package/dist/document/expr/index.d.ts +1 -0
  127. package/dist/document/expr/index.d.ts.map +1 -1
  128. package/dist/document/expr/index.js +2 -0
  129. package/dist/document/expr/index.js.map +1 -1
  130. package/dist/document/expr/typing.d.ts +24 -0
  131. package/dist/document/expr/typing.d.ts.map +1 -0
  132. package/dist/document/expr/typing.js +219 -0
  133. package/dist/document/expr/typing.js.map +1 -0
  134. package/dist/document/expr/when.d.ts +52 -0
  135. package/dist/document/expr/when.d.ts.map +1 -0
  136. package/dist/document/expr/when.js +215 -0
  137. package/dist/document/expr/when.js.map +1 -0
  138. package/dist/document/expr-type-findings.d.ts +5 -0
  139. package/dist/document/expr-type-findings.d.ts.map +1 -0
  140. package/dist/document/expr-type-findings.js +90 -0
  141. package/dist/document/expr-type-findings.js.map +1 -0
  142. package/dist/document/field-spec.d.ts.map +1 -1
  143. package/dist/document/field-spec.js +5 -0
  144. package/dist/document/field-spec.js.map +1 -1
  145. package/dist/document/findings.d.ts +33 -2
  146. package/dist/document/findings.d.ts.map +1 -1
  147. package/dist/document/findings.js +14 -17
  148. package/dist/document/findings.js.map +1 -1
  149. package/dist/document/index.d.ts +6 -3
  150. package/dist/document/index.d.ts.map +1 -1
  151. package/dist/document/index.js +5 -2
  152. package/dist/document/index.js.map +1 -1
  153. package/dist/document/linked-findings.js +1 -1
  154. package/dist/document/linked-findings.js.map +1 -1
  155. package/dist/document/migrate.d.ts.map +1 -1
  156. package/dist/document/migrate.js +10 -4
  157. package/dist/document/migrate.js.map +1 -1
  158. package/dist/document/rule-line.d.ts +7 -3
  159. package/dist/document/rule-line.d.ts.map +1 -1
  160. package/dist/document/rule-line.js +17 -2
  161. package/dist/document/rule-line.js.map +1 -1
  162. package/dist/document/rule-reads.d.ts.map +1 -1
  163. package/dist/document/rule-reads.js +3 -1
  164. package/dist/document/rule-reads.js.map +1 -1
  165. package/dist/document/rules.d.ts +30 -0
  166. package/dist/document/rules.d.ts.map +1 -1
  167. package/dist/document/rules.js +74 -0
  168. package/dist/document/rules.js.map +1 -1
  169. package/dist/document/schema.d.ts +80 -2
  170. package/dist/document/schema.d.ts.map +1 -1
  171. package/dist/document/schema.js +90 -4
  172. package/dist/document/schema.js.map +1 -1
  173. package/dist/document/to-document.d.ts.map +1 -1
  174. package/dist/document/to-document.js +3 -1
  175. package/dist/document/to-document.js.map +1 -1
  176. package/dist/document.schema.json +304 -5
  177. package/dist/formats.d.ts +21 -13
  178. package/dist/formats.d.ts.map +1 -1
  179. package/dist/formats.js +21 -13
  180. package/dist/formats.js.map +1 -1
  181. package/dist/import/coerce.d.ts.map +1 -1
  182. package/dist/import/coerce.js +16 -4
  183. package/dist/import/coerce.js.map +1 -1
  184. package/dist/index.d.ts +6 -5
  185. package/dist/index.d.ts.map +1 -1
  186. package/dist/index.js +5 -3
  187. package/dist/index.js.map +1 -1
  188. package/dist/invariants/types.d.ts +7 -2
  189. package/dist/invariants/types.d.ts.map +1 -1
  190. package/dist/kind-list.d.ts +1 -0
  191. package/dist/kind-list.d.ts.map +1 -1
  192. package/dist/kind-list.js +2 -1
  193. package/dist/kind-list.js.map +1 -1
  194. package/dist/lines.d.ts.map +1 -1
  195. package/dist/lines.js +6 -2
  196. package/dist/lines.js.map +1 -1
  197. package/dist/mutations/form.d.ts +2 -0
  198. package/dist/mutations/form.d.ts.map +1 -1
  199. package/dist/mutations/form.js +6 -3
  200. package/dist/mutations/form.js.map +1 -1
  201. package/dist/mutations/types.d.ts +9 -0
  202. package/dist/mutations/types.d.ts.map +1 -1
  203. package/dist/mutations/vote-suggestion.d.ts +67 -0
  204. package/dist/mutations/vote-suggestion.d.ts.map +1 -0
  205. package/dist/mutations/vote-suggestion.js +36 -0
  206. package/dist/mutations/vote-suggestion.js.map +1 -0
  207. package/dist/navigate.d.ts +7 -3
  208. package/dist/navigate.d.ts.map +1 -1
  209. package/dist/navigate.js +7 -3
  210. package/dist/navigate.js.map +1 -1
  211. package/dist/ops/lineage.d.ts +11 -1
  212. package/dist/ops/lineage.d.ts.map +1 -1
  213. package/dist/ops/lineage.js +6 -2
  214. package/dist/ops/lineage.js.map +1 -1
  215. package/dist/ops/suggestions.d.ts +33 -1
  216. package/dist/ops/suggestions.d.ts.map +1 -1
  217. package/dist/ops/suggestions.js +44 -2
  218. package/dist/ops/suggestions.js.map +1 -1
  219. package/dist/ops/types.d.ts +13 -0
  220. package/dist/ops/types.d.ts.map +1 -1
  221. package/dist/ops/types.js.map +1 -1
  222. package/dist/place-names.d.ts +42 -0
  223. package/dist/place-names.d.ts.map +1 -0
  224. package/dist/place-names.js +43 -0
  225. package/dist/place-names.js.map +1 -0
  226. package/dist/places.d.ts +2 -0
  227. package/dist/places.d.ts.map +1 -1
  228. package/dist/places.js +2 -0
  229. package/dist/places.js.map +1 -1
  230. package/dist/scaffold/agents.d.ts +9 -1
  231. package/dist/scaffold/agents.d.ts.map +1 -1
  232. package/dist/scaffold/agents.js +33 -2
  233. package/dist/scaffold/agents.js.map +1 -1
  234. package/dist/scaffold/project.js +1 -1
  235. package/dist/schema/choice-words.d.ts.map +1 -1
  236. package/dist/schema/choice-words.js +3 -0
  237. package/dist/schema/choice-words.js.map +1 -1
  238. package/dist/starting-document.d.ts +73 -0
  239. package/dist/starting-document.d.ts.map +1 -0
  240. package/dist/starting-document.js +645 -0
  241. package/dist/starting-document.js.map +1 -0
  242. package/dist/store.d.ts +50 -0
  243. package/dist/store.d.ts.map +1 -1
  244. package/dist/store.js +167 -27
  245. package/dist/store.js.map +1 -1
  246. package/dist/sync/connection.d.ts +20 -1
  247. package/dist/sync/connection.d.ts.map +1 -1
  248. package/dist/sync/connection.js +198 -34
  249. package/dist/sync/connection.js.map +1 -1
  250. package/dist/sync/engine.d.ts +13 -4
  251. package/dist/sync/engine.d.ts.map +1 -1
  252. package/dist/sync/engine.js +10 -6
  253. package/dist/sync/engine.js.map +1 -1
  254. package/dist/sync/google-calendar.d.ts +8 -0
  255. package/dist/sync/google-calendar.d.ts.map +1 -1
  256. package/dist/sync/google-calendar.js +16 -4
  257. package/dist/sync/google-calendar.js.map +1 -1
  258. package/dist/sync/guests.d.ts +44 -0
  259. package/dist/sync/guests.d.ts.map +1 -0
  260. package/dist/sync/guests.js +57 -0
  261. package/dist/sync/guests.js.map +1 -0
  262. package/dist/sync/notion-types.d.ts +7 -0
  263. package/dist/sync/notion-types.d.ts.map +1 -1
  264. package/dist/sync/notion-types.js +6 -1
  265. package/dist/sync/notion-types.js.map +1 -1
  266. package/dist/sync/recurrence.d.ts +41 -0
  267. package/dist/sync/recurrence.d.ts.map +1 -0
  268. package/dist/sync/recurrence.js +192 -0
  269. package/dist/sync/recurrence.js.map +1 -0
  270. package/dist/sync/types.d.ts +9 -0
  271. package/dist/sync/types.d.ts.map +1 -1
  272. package/dist/sync/types.js.map +1 -1
  273. package/dist/testing.js +1 -1
  274. package/dist/testing.js.map +1 -1
  275. package/dist/theme/contrast.d.ts +2 -0
  276. package/dist/theme/contrast.d.ts.map +1 -1
  277. package/dist/theme/contrast.js +4 -0
  278. package/dist/theme/contrast.js.map +1 -1
  279. package/dist/theme/ground.d.ts +1 -2
  280. package/dist/theme/ground.d.ts.map +1 -1
  281. package/dist/theme/ground.js +2 -5
  282. package/dist/theme/ground.js.map +1 -1
  283. package/dist/theme/marks.d.ts +14 -2
  284. package/dist/theme/marks.d.ts.map +1 -1
  285. package/dist/theme/marks.js +20 -3
  286. package/dist/theme/marks.js.map +1 -1
  287. package/dist/theme/types.d.ts +22 -0
  288. package/dist/theme/types.d.ts.map +1 -1
  289. package/dist/theme/types.js.map +1 -1
  290. package/dist/tidying.d.ts +30 -4
  291. package/dist/tidying.d.ts.map +1 -1
  292. package/dist/tidying.js +110 -5
  293. package/dist/tidying.js.map +1 -1
  294. package/dist/trait-vocabulary.d.ts +17 -0
  295. package/dist/trait-vocabulary.d.ts.map +1 -0
  296. package/dist/trait-vocabulary.js +16 -0
  297. package/dist/trait-vocabulary.js.map +1 -0
  298. package/dist/traits.d.ts +79 -0
  299. package/dist/traits.d.ts.map +1 -0
  300. package/dist/traits.js +468 -0
  301. package/dist/traits.js.map +1 -0
  302. package/dist/typed.d.ts +4 -0
  303. package/dist/typed.d.ts.map +1 -1
  304. package/dist/typed.js.map +1 -1
  305. package/dist/version.d.ts +1 -1
  306. package/dist/version.js +1 -1
  307. package/package.json +11 -1
package/dist/store.js CHANGED
@@ -13,12 +13,13 @@ import { compileMutation } from "./mutations/define-mutation.js";
13
13
  import { wouldRun } from "./mutations/would-run.js";
14
14
  import { actSpelledAs, nearestAct } from "./mutations/act-spelling.js";
15
15
  import { InvalidArguments } from "./mutations/invalid-arguments.js";
16
- import { deriveMutations, derivedVia, removeVia } from "./mutations/derive-edits.js";
16
+ import { deriveMutations, derivedVia, editMutationName, editVia, removeVia } from "./mutations/derive-edits.js";
17
17
  import { repeatingFields, repeatMakes } from "./mutations/derive-repeats.js";
18
18
  import { namesMade, resolveMade } from "./mutations/made.js";
19
19
  import { OperationLog } from "./ops/log.js";
20
20
  import { lineageOf } from "./ops/lineage.js";
21
21
  import { KEEP_LINK, keepLinkMutation, keepLinkVia } from "./mutations/keep-link.js";
22
+ import { VOTE_SUGGESTION, VOTING, voteSuggestionMutation } from "./mutations/vote-suggestion.js";
22
23
  import { TYPE_TEXT, TYPING } from "./text/typing-hook.js";
23
24
  import { declinedIn, isAutomated, tidyingRefusal } from "./ops/tidying.js";
24
25
  import { nameOfAuthor } from "./who.js";
@@ -36,6 +37,8 @@ import { dayOf } from "./clock.js";
36
37
  import { pluralOf } from "./schema/define-node.js";
37
38
  import { linkedReaderOf } from "./schema/linked.js";
38
39
  import { onPublish, PUBLISH, PUBLISHING } from "./publish.js";
40
+ import { FORMATS } from "./format-error.js";
41
+ import { formatStamp } from "./formats.js";
39
42
  /** A change asked of a store of the past (Roadmap f-playback): the past is read, never changed. */
40
43
  export class PastError extends GraphError {
41
44
  constructor(at) {
@@ -182,11 +185,15 @@ export class Store {
182
185
  keeper;
183
186
  /** "Type text", when the store answers to its own (Roadmap f-collab-text). */
184
187
  typer;
188
+ /** "Vote on a suggestion" (Roadmap f-suggestion-votes), when the app declared no act by that name. */
189
+ voter;
185
190
  invariants;
186
191
  invariantOptions;
187
192
  policy;
188
193
  /** When edits reach readers (`StoreOptions.publishing`). */
189
194
  publishing;
195
+ /** What the declaration says of suggestions as a whole (`StoreOptions.tidying`). */
196
+ tidying;
190
197
  /**
191
198
  * The intelligence the app declared, so a surface can read the DOORS.
192
199
  *
@@ -249,6 +256,7 @@ export class Store {
249
256
  this.ticked = { day: isoToday(this.timeZone), on: isoToday(this.timeZone) };
250
257
  this.policy = options.policy;
251
258
  this.publishing = options.publishing;
259
+ this.tidying = options.tidying;
252
260
  this.by = options.principal ?? HUMAN;
253
261
  this.intelligence = options.intelligence ?? [];
254
262
  for (const provider of options.intelligence ?? []) {
@@ -286,6 +294,11 @@ export class Store {
286
294
  this.keeper = keepLinkMutation(options.schema);
287
295
  this.mutations.set(KEEP_LINK, this.keeper);
288
296
  }
297
+ // "Vote on a suggestion" (Roadmap f-suggestion-votes): answered by every store, offered by no list. An app's own `vote-suggestion` keeps its name.
298
+ if (!options.past && !this.mutations.has(VOTE_SUGGESTION)) {
299
+ this.voter = voteSuggestionMutation();
300
+ this.mutations.set(VOTE_SUGGESTION, this.voter);
301
+ }
289
302
  const horizon = given("horizon", options.horizon);
290
303
  const validate = { validate: this.validate };
291
304
  if (options.log) {
@@ -494,7 +507,7 @@ export class Store {
494
507
  return mutation.derived !== undefined && this.modules.disabledKinds.has(mutation.derived.kind);
495
508
  }
496
509
  allMutations() {
497
- return [...this.mutations.values(), ...(this.publisher ? [this.publisher] : [])].filter((mutation) => mutation !== this.keeper && mutation !== this.typer && !this.mutationDisabled(mutation));
510
+ return [...this.mutations.values(), ...(this.publisher ? [this.publisher] : [])].filter((mutation) => mutation !== this.keeper && mutation !== this.typer && mutation !== this.voter && !this.mutationDisabled(mutation));
498
511
  }
499
512
  /** The declared acts a derived act resolves its permission through; keeping a link, those that make or break its relation. */
500
513
  viaOf(mutation, call, principal) {
@@ -565,6 +578,9 @@ export class Store {
565
578
  // Publishing is the declaration's `publishers`, not a grant's.
566
579
  if (mutation && mutation === this.publisher)
567
580
  return PUBLISHING.verdict(this, principal);
581
+ // A vote on a suggestion is anybody's who may see it (Roadmap f-suggestion-votes): whether they do is judged where the op is made.
582
+ if (mutation && mutation === this.voter)
583
+ return { ok: true };
568
584
  /*
569
585
  * KEEPING A LINK ONLY THE DERIVED ACTS MAKE is judged as they are: on
570
586
  * the record it leaves, so riding everybody's grant it reaches only the
@@ -598,6 +614,17 @@ export class Store {
598
614
  mutation !== undefined && (mutation.derived !== undefined || mutation === this.typer || keeping) ? () => this.madeBy(mutation, call, id) : undefined,
599
615
  // Taking a record out, or folding it away, rides the making of the kind only over what this principal made (W-201).
600
616
  mutation?.derived !== undefined && UNMAKING.has(mutation.derived.act));
617
+ /*
618
+ * A VALUE CARRIED ACROSS A LINK (Roadmap f-acts-update-linked) is each
619
+ * far record's own edit: the act is refused, naming the record, where
620
+ * this principal may not change one of the records its subject is
621
+ * linked to — judged here, before `apply`, as every grant is.
622
+ */
623
+ if (verdict.ok && mutation?.writesLinked && typeof id === "string") {
624
+ const refused = this.refusesLinked(mutation, id, principal);
625
+ if (refused)
626
+ return refused;
627
+ }
601
628
  if (!verdict.ok || !mutation?.derived || !this.repeatMakes(mutation, call, id, principal))
602
629
  return verdict;
603
630
  /*
@@ -608,6 +635,44 @@ export class Store {
608
635
  */
609
636
  return permits(this.policy, principal, call.name, kind, removeVia([...this.mutations.values()], mutation.derived.kind), typeof id === "string" ? id : undefined, this.words);
610
637
  }
638
+ /**
639
+ * The first record linked to the subject that this principal may not
640
+ * change, as a refusal of the act naming it (Roadmap f-acts-update-linked);
641
+ * undefined when every record the act would carry a value to is theirs to
642
+ * edit. Each is judged as its kind's own edit is — the grants, the acts the
643
+ * edit rides, what the principal made — over the graph as they see it.
644
+ */
645
+ refusesLinked(mutation, subjectId, principal) {
646
+ const served = isSystem(principal) ? this.graph : seenBy(this, principal).graph;
647
+ const subject = served.getNode(subjectId);
648
+ if (!subject)
649
+ return undefined;
650
+ const all = [...this.mutations.values()];
651
+ for (const relation of Object.keys(mutation.writesLinked ?? {})) {
652
+ const declaredHere = this.schema.tryDefinition(subject.kind)?.edges?.[relation] !== undefined;
653
+ const far = declaredHere ? served.out(subject.id, relation) : served.in(subject.id, relation);
654
+ for (const node of far) {
655
+ const edit = editMutationName(node.kind);
656
+ const own = this.mutations.get(edit);
657
+ const verdict = own ? this.permits({ name: edit, args: { [own.subject?.arg ?? "id"]: node.id } }, principal) : permits(this.policy, principal, edit, node.kind, editVia(this.schema, all, node.kind), node.id, this.words);
658
+ if (verdict.ok)
659
+ continue;
660
+ const title = this.words.act?.(mutation.name) ?? mutation.name;
661
+ const label = (this.labels ??= new LabelIndex(this.graph)).labelOf(node.id) ?? node.id;
662
+ const noun = this.words.noun?.(node.kind) ?? node.kind;
663
+ return {
664
+ ok: false,
665
+ refusal: {
666
+ mutation: mutation.name,
667
+ kind: subject.kind,
668
+ wouldNeed: verdict.refusal.wouldNeed,
669
+ message: `Not permitted: “${title}” would change “${label}”, ${/^[aeiou]/i.test(noun) ? "an" : "a"} ${noun} that is not yours to change. ${verdict.refusal.message}`,
670
+ },
671
+ };
672
+ }
673
+ }
674
+ return undefined;
675
+ }
611
676
  /** Whether a call of a repeat act makes a record (`repeatMakes`), read off the series as this principal is served it. */
612
677
  repeatMakes(mutation, call, id, principal) {
613
678
  const act = mutation.derived.act;
@@ -1116,6 +1181,23 @@ export class Store {
1116
1181
  Object.assign(twin, { typer, mutations: typer ? new Map(this.mutations).set(TYPE_TEXT, typer) : new Map(this.mutations) });
1117
1182
  return twin;
1118
1183
  }
1184
+ /**
1185
+ * Whether these calls, as this author with this lineage, would be kept
1186
+ * rather than refused — rehearsed, told to nobody. What a vote asks
1187
+ * before it has the app's own actor make a suggestion that reached
1188
+ * `autoAcceptAt` (Roadmap f-suggestion-votes): a record a person said to
1189
+ * leave alone, or a change a person declined meanwhile, leaves the
1190
+ * suggestion waiting for someone who may, and the vote stands.
1191
+ */
1192
+ wouldKeep(calls, author, lineage) {
1193
+ try {
1194
+ this.rehearsal().applying(calls, { author, lineage });
1195
+ return true;
1196
+ }
1197
+ catch {
1198
+ return false;
1199
+ }
1200
+ }
1119
1201
  previewPrimitives(primitives, meta) {
1120
1202
  const before = this.violations(meta.context);
1121
1203
  const trial = Graph.from(this.schema, this.graph.snapshot());
@@ -1178,20 +1260,29 @@ export class Store {
1178
1260
  * seat holds say what it may do at all; `may` narrows what THIS provider
1179
1261
  * was brought in for, and the narrower of the two wins.
1180
1262
  */
1181
- const permitted = (call) => {
1182
- const verdict = this.policy || (this.publisher && call.name === PUBLISH) ? this.permits(call, author) : undefined;
1263
+ const permitted = (call, as = author) => {
1264
+ const verdict = this.policy || (this.publisher && call.name === PUBLISH) ? this.permits(call, as) : undefined;
1183
1265
  if (verdict && !verdict.ok)
1184
- this.refuse(verdict.refusal, author.id);
1266
+ this.refuse(verdict.refusal, as.id);
1185
1267
  };
1186
- const declared = (call) => {
1187
- const refusal = this.refusesAgent(call, author) ?? this.namesTurnedOff(call, author);
1268
+ const declared = (call, as = author) => {
1269
+ const refusal = this.refusesAgent(call, as) ?? this.namesTurnedOff(call, as);
1188
1270
  if (refusal)
1189
- this.refuse(refusal, author.id);
1271
+ this.refuse(refusal, as.id);
1190
1272
  };
1191
1273
  /* A call that names a record an earlier call makes (`{ $made: n }`, FR-193) is asked once that record is there to name. */
1192
1274
  const naming = calls.map(namesMade);
1193
1275
  calls.forEach((call, at) => naming[at] || permitted(call));
1194
1276
  calls.forEach((call, at) => naming[at] || declared(call));
1277
+ /*
1278
+ * THE GESTURE'S CALLS, AND WHAT ONE OF THEM HAS THE APP'S OWN ACTOR MAKE
1279
+ * (Roadmap f-suggestion-votes): a vote that brings a suggestion to the
1280
+ * declaration's `autoAcceptAt` queues the suggestion's calls behind it,
1281
+ * as the actor, in this same gesture — one batch, one undo, one answer
1282
+ * to whoever sent it — and the people who voted decided it, so the
1283
+ * declaration's `tidying` does not stand in the actor's way (`decided`).
1284
+ */
1285
+ const queue = calls.map((sent) => ({ sent, as: author, lineage, decided: false }));
1195
1286
  /*
1196
1287
  * THE APP'S OWN ACTOR IS HELD TO WHAT PEOPLE SAID: a change a person
1197
1288
  * declined is not made again, a record a person said to leave alone is
@@ -1216,38 +1307,55 @@ export class Store {
1216
1307
  const renamed = new Map();
1217
1308
  const rename = (value) => (typeof value === "string" ? (renamed.get(value) ?? value) : Array.isArray(value) ? value.map(rename) : value);
1218
1309
  try {
1219
- for (const [at, sent] of calls.entries()) {
1310
+ for (let at = 0; at < queue.length; at++) {
1311
+ const { sent, as, lineage: lined, decided } = queue[at];
1312
+ const named = at < naming.length ? naming[at] : namesMade(sent);
1220
1313
  const { mintedHint: hint, ...plain } = sent;
1221
1314
  const asked = renamed.size ? { ...plain, args: Object.fromEntries(Object.entries(plain.args).map(([key, value]) => [key, rename(value)])) } : plain;
1222
- const call = naming[at] ? resolveMade(asked, madeIds, at) : asked;
1223
- if (naming[at])
1224
- (permitted(call), declared(call));
1315
+ const call = named ? resolveMade(asked, madeIds, at) : asked;
1316
+ if (named || decided)
1317
+ (permitted(call, as), declared(call, as));
1225
1318
  const definition = this.mutation(call.name);
1226
1319
  if (definition === this.publisher) {
1227
- const op = PUBLISHING.op(this, { id: this.nextId(), at: this.now() }, call, batch, author, options);
1320
+ const op = PUBLISHING.op(this, { id: this.nextId(), at: this.now() }, call, batch, as, options);
1228
1321
  ops.push(op);
1229
1322
  intents.push(op.intent);
1230
1323
  continue;
1231
1324
  }
1232
- const missing = this.missingFor(call, author);
1325
+ if (definition === this.voter) {
1326
+ // A vote on a suggestion (Roadmap f-suggestion-votes): its op, judged as the voter sees the suggestion, and nothing of the graph.
1327
+ if (!VOTING.op)
1328
+ throw new GraphError("Load @graview/core/tidying to vote on a suggestion.");
1329
+ const op = VOTING.op(this, { id: this.nextId(), at: this.now() }, call, batch, as, options);
1330
+ this.log.append(op);
1331
+ ops.push(op);
1332
+ intents.push(op.intent);
1333
+ // At the declaration's `autoAcceptAt`, the suggestion is made here, in the same gesture, by the app's own actor — when the actor would be let.
1334
+ const accept = VOTING.accepting?.(this, op);
1335
+ if (accept && this.wouldKeep(accept.calls, accept.author, accept.lineage))
1336
+ for (const made of accept.calls)
1337
+ queue.push({ sent: made, as: accept.author, lineage: lineageOf(accept.lineage), decided: true });
1338
+ continue;
1339
+ }
1340
+ const missing = this.missingFor(call, as);
1233
1341
  if (missing)
1234
1342
  throw missing;
1235
1343
  let compiled;
1236
1344
  try {
1237
- compiled = compileMutation(this.graph, definition, call.args, { ...this.sighted(author), hint });
1345
+ compiled = compileMutation(this.graph, definition, call.args, { ...this.sighted(as), hint });
1238
1346
  }
1239
1347
  catch (error) {
1240
1348
  // A press the act's own input refused: the watch tells a form filled wrongly from an act offered whole.
1241
1349
  if (error instanceof InvalidArguments && !this.rehearsing)
1242
- tellTheWatchOfAnInputRefusal(error, author.id);
1350
+ tellTheWatchOfAnInputRefusal(error, as.id);
1243
1351
  throw error;
1244
1352
  }
1245
1353
  if (hint !== undefined && compiled.firstMinted !== undefined && compiled.firstMinted !== hint)
1246
1354
  renamed.set(hint, compiled.firstMinted);
1247
- if (automated) {
1248
- const refused = tidyingRefusal(this.schema, this.graph, compiled.primitives, this.authorName(author), { declaration: !this.suggesting });
1355
+ if (isAutomated(as)) {
1356
+ const refused = tidyingRefusal(this.schema, this.graph, compiled.primitives, this.authorName(as), { declaration: !this.suggesting && !decided });
1249
1357
  if (refused)
1250
- this.refuse({ mutation: call.name, message: refused, wouldNeed: [] }, author.id);
1358
+ this.refuse({ mutation: call.name, message: refused, wouldNeed: [] }, as.id);
1251
1359
  }
1252
1360
  /*
1253
1361
  * AN ACT THAT DID NOTHING DOES NOT GO IN THE HISTORY.
@@ -1268,7 +1376,7 @@ export class Store {
1268
1376
  id: this.nextId(),
1269
1377
  seq: this.log.length,
1270
1378
  batch,
1271
- author,
1379
+ author: as,
1272
1380
  /*
1273
1381
  * THE ACT'S OWN SENTENCE, kept; what the gesture was for goes
1274
1382
  * beside it (FR-18). An act with no `describe` has no sentence of
@@ -1292,7 +1400,7 @@ export class Store {
1292
1400
  ...given("mergedInto", compiled.mergedInto),
1293
1401
  at: this.now(),
1294
1402
  ...given("via", options.via),
1295
- ...given("lineage", lineage),
1403
+ ...given("lineage", lined),
1296
1404
  };
1297
1405
  this.graph.applyPrimitives(op.primitives);
1298
1406
  this.log.append(op);
@@ -1679,6 +1787,13 @@ export class Store {
1679
1787
  */
1680
1788
  const judged = this.rootOf(target);
1681
1789
  const by = actingAs(judged.author).id;
1790
+ // A vote is its voter's to take back (Roadmap f-suggestion-votes), the system's, or a seat's the policy grants every act.
1791
+ if (judged.vote !== undefined) {
1792
+ if (this.policy && !isSystem(author) && by !== actingAs(author).id && !this.grantsEveryAct(author)) {
1793
+ return { mutation: "undo", wouldNeed: [], message: `Not permitted to undo "${target.intent}": only whoever voted takes their vote back.` };
1794
+ }
1795
+ continue;
1796
+ }
1682
1797
  if (!judged.mutation) {
1683
1798
  // A change that changed nothing (a suggestion, an announcement) is anybody's who sees it to take back: declining a suggestion is one.
1684
1799
  if (judged.primitives.length === 0)
@@ -2053,19 +2168,44 @@ export class Store {
2053
2168
  const pinned = [...(options.pins ?? []), ...readers].filter((pin) => Number.isInteger(pin));
2054
2169
  if (pinned.length > 0)
2055
2170
  seq = Math.min(seq, ...pinned);
2056
- seq = Math.min(Math.max(seq, this.log.horizon), this.log.length);
2057
- // Never inside a gesture: back to where the batch it would split began.
2058
- while (seq > this.log.horizon && seq < this.log.length && ops[seq - this.log.horizon].batch === ops[seq - 1 - this.log.horizon].batch)
2059
- seq--;
2171
+ const format = options.format ?? FORMATS.snapshot;
2172
+ if (!Number.isInteger(format) || format < 1 || format > FORMATS.snapshot) {
2173
+ throw new RangeError(`Cannot write a checkpoint in snapshot format ${String(format)}: this build writes formats 1 to ${FORMATS.snapshot}.`);
2174
+ }
2175
+ // Never past the head or behind the horizon, and never inside a gesture: back to where the batch it would split began.
2176
+ const settled = (asked) => {
2177
+ let at = Math.min(Math.max(asked, this.log.horizon), this.log.length);
2178
+ while (at > this.log.horizon && at < this.log.length && ops[at - this.log.horizon].batch === ops[at - 1 - this.log.horizon].batch)
2179
+ at--;
2180
+ return at;
2181
+ };
2182
+ seq = settled(seq);
2060
2183
  if (seq <= this.log.horizon)
2061
2184
  return undefined;
2062
2185
  const verified = this.verify();
2063
2186
  if (!verified.ok)
2064
2187
  throw new GraphError(`Cannot compact a store that does not verify: ${verified.reason}`);
2188
+ let checkpoint = this.checkpointWithPublished(seq);
2065
2189
  /*
2066
- * And the app as readers were served it at each published seq behind it they may be served again: folded here where the
2067
- * log still holds it, carried from the checkpoint before where it does not, so a horizon past old publishes loses none.
2190
+ * READABLE AS THE FORMAT ASKED FOR (FR-31): a checkpoint keeping the app as readers are served at a seq behind it
2191
+ * (`published`) is snapshot format 2, which a build before it would misread (`formatStamp`). Asked for format 1,
2192
+ * the horizon is held at the earliest such seq instead — and again where that lands past another — until the stamp
2193
+ * says 1; the checkpoint's seq says where it stopped, and none is made when that is the horizon already.
2068
2194
  */
2195
+ while (formatStamp(checkpoint).formats.snapshot > format) {
2196
+ seq = settled(Math.min(...checkpoint.published.map((kept) => kept.seq)));
2197
+ if (seq <= this.log.horizon)
2198
+ return undefined;
2199
+ checkpoint = this.checkpointWithPublished(seq);
2200
+ }
2201
+ return checkpoint;
2202
+ }
2203
+ /**
2204
+ * The checkpoint at `seq`, and the app as readers were served it at each published seq behind it they may be served
2205
+ * again: folded here where the log still holds it, carried from the checkpoint before where it does not, so a horizon
2206
+ * past old publishes loses none.
2207
+ */
2208
+ checkpointWithPublished(seq) {
2069
2209
  const carried = this.log.horizonEpoch()?.published ?? [];
2070
2210
  const published = (PUBLISHING.behind?.(this, seq) ?? []).flatMap((at) => (at >= this.log.horizon ? [this.epochAt(at)] : carried.filter((one) => one.seq === at)));
2071
2211
  return { ...this.epochAt(seq), ...(published.length > 0 && { published }) };