@graview/core 0.1.62 → 0.1.64

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 (340) hide show
  1. package/dist/app.d.ts +12 -0
  2. package/dist/app.d.ts.map +1 -1
  3. package/dist/app.js.map +1 -1
  4. package/dist/arranging.js +2 -2
  5. package/dist/arranging.js.map +1 -1
  6. package/dist/cli/check/index.d.ts +1 -0
  7. package/dist/cli/check/index.d.ts.map +1 -1
  8. package/dist/cli/check/index.js +1 -0
  9. package/dist/cli/check/index.js.map +1 -1
  10. package/dist/cli/check/workflows.d.ts +11 -0
  11. package/dist/cli/check/workflows.d.ts.map +1 -0
  12. package/dist/cli/check/workflows.js +20 -0
  13. package/dist/cli/check/workflows.js.map +1 -0
  14. package/dist/cli/check.d.ts.map +1 -1
  15. package/dist/cli/check.js +2 -1
  16. package/dist/cli/check.js.map +1 -1
  17. package/dist/cli/describe.d.ts.map +1 -1
  18. package/dist/cli/describe.js +31 -1
  19. package/dist/cli/describe.js.map +1 -1
  20. package/dist/cli/document-schema.d.ts.map +1 -1
  21. package/dist/cli/document-schema.js +9 -0
  22. package/dist/cli/document-schema.js.map +1 -1
  23. package/dist/compose.d.ts +1 -1
  24. package/dist/compose.d.ts.map +1 -1
  25. package/dist/document/amount.d.ts +1 -1
  26. package/dist/document/amount.d.ts.map +1 -1
  27. package/dist/document/amount.js +2 -2
  28. package/dist/document/amount.js.map +1 -1
  29. package/dist/document/blocks.d.ts.map +1 -1
  30. package/dist/document/blocks.js +17 -1
  31. package/dist/document/blocks.js.map +1 -1
  32. package/dist/document/brand-check.d.ts.map +1 -1
  33. package/dist/document/brand-check.js +8 -2
  34. package/dist/document/brand-check.js.map +1 -1
  35. package/dist/document/compile-checked.d.ts.map +1 -1
  36. package/dist/document/compile-checked.js +4 -2
  37. package/dist/document/compile-checked.js.map +1 -1
  38. package/dist/document/compile.d.ts.map +1 -1
  39. package/dist/document/compile.js +48 -9
  40. package/dist/document/compile.js.map +1 -1
  41. package/dist/document/compiled.d.ts +14 -0
  42. package/dist/document/compiled.d.ts.map +1 -1
  43. package/dist/document/compiled.js +39 -4
  44. package/dist/document/compiled.js.map +1 -1
  45. package/dist/document/connection-findings.js +93 -21
  46. package/dist/document/connection-findings.js.map +1 -1
  47. package/dist/document/connection-spec.d.ts +54 -1
  48. package/dist/document/connection-spec.d.ts.map +1 -1
  49. package/dist/document/connection-spec.js +76 -8
  50. package/dist/document/connection-spec.js.map +1 -1
  51. package/dist/document/derives-findings.d.ts +4 -0
  52. package/dist/document/derives-findings.d.ts.map +1 -0
  53. package/dist/document/derives-findings.js +68 -0
  54. package/dist/document/derives-findings.js.map +1 -0
  55. package/dist/document/describe-forms.js +2 -2
  56. package/dist/document/describe-forms.js.map +1 -1
  57. package/dist/document/diff.d.ts +6 -0
  58. package/dist/document/diff.d.ts.map +1 -1
  59. package/dist/document/diff.js +43 -1
  60. package/dist/document/diff.js.map +1 -1
  61. package/dist/document/edit.d.ts +1 -1
  62. package/dist/document/edit.d.ts.map +1 -1
  63. package/dist/document/edit.js +264 -23
  64. package/dist/document/edit.js.map +1 -1
  65. package/dist/document/expr/evaluate.d.ts +7 -0
  66. package/dist/document/expr/evaluate.d.ts.map +1 -1
  67. package/dist/document/expr/evaluate.js +29 -2
  68. package/dist/document/expr/evaluate.js.map +1 -1
  69. package/dist/document/format-words.d.ts +1 -0
  70. package/dist/document/format-words.d.ts.map +1 -1
  71. package/dist/document/format-words.js +2 -2
  72. package/dist/document/format-words.js.map +1 -1
  73. package/dist/document/kept-in-step-with.d.ts +20 -0
  74. package/dist/document/kept-in-step-with.d.ts.map +1 -0
  75. package/dist/document/kept-in-step-with.js +38 -0
  76. package/dist/document/kept-in-step-with.js.map +1 -0
  77. package/dist/document/kept-in-step.d.ts.map +1 -1
  78. package/dist/document/kept-in-step.js +3 -1
  79. package/dist/document/kept-in-step.js.map +1 -1
  80. package/dist/document/money.d.ts +10 -2
  81. package/dist/document/money.d.ts.map +1 -1
  82. package/dist/document/money.js +14 -5
  83. package/dist/document/money.js.map +1 -1
  84. package/dist/document/policy-words.d.ts +2 -0
  85. package/dist/document/policy-words.d.ts.map +1 -1
  86. package/dist/document/policy-words.js +11 -1
  87. package/dist/document/policy-words.js.map +1 -1
  88. package/dist/document/schema.d.ts +46 -1
  89. package/dist/document/schema.d.ts.map +1 -1
  90. package/dist/document/schema.js +48 -8
  91. package/dist/document/schema.js.map +1 -1
  92. package/dist/document/template-parse.d.ts.map +1 -1
  93. package/dist/document/template-parse.js +7 -1
  94. package/dist/document/template-parse.js.map +1 -1
  95. package/dist/document/template.d.ts +2 -0
  96. package/dist/document/template.d.ts.map +1 -1
  97. package/dist/document/template.js +10 -1
  98. package/dist/document/template.js.map +1 -1
  99. package/dist/document/to-document.js +2 -2
  100. package/dist/document/to-document.js.map +1 -1
  101. package/dist/document/workflow-spec.d.ts +72 -0
  102. package/dist/document/workflow-spec.d.ts.map +1 -0
  103. package/dist/document/workflow-spec.js +45 -0
  104. package/dist/document/workflow-spec.js.map +1 -0
  105. package/dist/document.schema.json +883 -4
  106. package/dist/head.d.ts +81 -0
  107. package/dist/head.d.ts.map +1 -0
  108. package/dist/head.js +123 -0
  109. package/dist/head.js.map +1 -0
  110. package/dist/history.d.ts.map +1 -1
  111. package/dist/history.js +2 -0
  112. package/dist/history.js.map +1 -1
  113. package/dist/index.d.ts +7 -5
  114. package/dist/index.d.ts.map +1 -1
  115. package/dist/index.js +4 -3
  116. package/dist/index.js.map +1 -1
  117. package/dist/installation.d.ts +1 -1
  118. package/dist/job-findings.d.ts +5 -0
  119. package/dist/job-findings.d.ts.map +1 -1
  120. package/dist/job-findings.js +9 -3
  121. package/dist/job-findings.js.map +1 -1
  122. package/dist/keep-linked.d.ts +45 -0
  123. package/dist/keep-linked.d.ts.map +1 -0
  124. package/dist/keep-linked.js +188 -0
  125. package/dist/keep-linked.js.map +1 -0
  126. package/dist/mutations/derive-edits.js +2 -2
  127. package/dist/mutations/propose-changes.d.ts +24 -0
  128. package/dist/mutations/propose-changes.d.ts.map +1 -0
  129. package/dist/mutations/propose-changes.js +30 -0
  130. package/dist/mutations/propose-changes.js.map +1 -0
  131. package/dist/mutations/vote-suggestion.d.ts +30 -0
  132. package/dist/mutations/vote-suggestion.d.ts.map +1 -1
  133. package/dist/mutations/vote-suggestion.js +23 -0
  134. package/dist/mutations/vote-suggestion.js.map +1 -1
  135. package/dist/mutations/words.js +3 -3
  136. package/dist/ops/digest.d.ts +3 -1
  137. package/dist/ops/digest.d.ts.map +1 -1
  138. package/dist/ops/digest.js +7 -2
  139. package/dist/ops/digest.js.map +1 -1
  140. package/dist/ops/lineage.d.ts +19 -2
  141. package/dist/ops/lineage.d.ts.map +1 -1
  142. package/dist/ops/lineage.js +8 -4
  143. package/dist/ops/lineage.js.map +1 -1
  144. package/dist/ops/log.d.ts.map +1 -1
  145. package/dist/ops/log.js +13 -2
  146. package/dist/ops/log.js.map +1 -1
  147. package/dist/ops/suggestions.d.ts +47 -5
  148. package/dist/ops/suggestions.d.ts.map +1 -1
  149. package/dist/ops/suggestions.js +88 -13
  150. package/dist/ops/suggestions.js.map +1 -1
  151. package/dist/ops/tidying.d.ts.map +1 -1
  152. package/dist/ops/tidying.js +6 -1
  153. package/dist/ops/tidying.js.map +1 -1
  154. package/dist/ops/types.d.ts +28 -0
  155. package/dist/ops/types.d.ts.map +1 -1
  156. package/dist/ops/undo.d.ts.map +1 -1
  157. package/dist/ops/undo.js +17 -0
  158. package/dist/ops/undo.js.map +1 -1
  159. package/dist/permissions/policy.d.ts +7 -0
  160. package/dist/permissions/policy.d.ts.map +1 -1
  161. package/dist/permissions/policy.js +12 -0
  162. package/dist/permissions/policy.js.map +1 -1
  163. package/dist/permissions/types.d.ts +27 -0
  164. package/dist/permissions/types.d.ts.map +1 -1
  165. package/dist/permissions/types.js.map +1 -1
  166. package/dist/places.d.ts +10 -0
  167. package/dist/places.d.ts.map +1 -1
  168. package/dist/places.js +35 -1
  169. package/dist/places.js.map +1 -1
  170. package/dist/scene.d.ts +2 -0
  171. package/dist/scene.d.ts.map +1 -1
  172. package/dist/scene.js +2 -0
  173. package/dist/scene.js.map +1 -1
  174. package/dist/schema/amount-mark.d.ts +2 -0
  175. package/dist/schema/amount-mark.d.ts.map +1 -1
  176. package/dist/schema/amount-mark.js.map +1 -1
  177. package/dist/schema/amount-words.js +2 -2
  178. package/dist/schema/amount-words.js.map +1 -1
  179. package/dist/schema/choice-words.js +2 -2
  180. package/dist/schema/define-node.d.ts +8 -2
  181. package/dist/schema/define-node.d.ts.map +1 -1
  182. package/dist/schema/define-node.js +2 -2
  183. package/dist/schema/define-node.js.map +1 -1
  184. package/dist/schema/humanize.d.ts +17 -0
  185. package/dist/schema/humanize.d.ts.map +1 -1
  186. package/dist/schema/humanize.js +28 -1
  187. package/dist/schema/humanize.js.map +1 -1
  188. package/dist/schema/reading.d.ts +8 -0
  189. package/dist/schema/reading.d.ts.map +1 -1
  190. package/dist/schema/reading.js +16 -2
  191. package/dist/schema/reading.js.map +1 -1
  192. package/dist/schema/types.d.ts +8 -0
  193. package/dist/schema/types.d.ts.map +1 -1
  194. package/dist/schema/value-words.js +2 -2
  195. package/dist/schema/value-words.js.map +1 -1
  196. package/dist/starting-document.d.ts +16 -3
  197. package/dist/starting-document.d.ts.map +1 -1
  198. package/dist/starting-document.js +32 -5
  199. package/dist/starting-document.js.map +1 -1
  200. package/dist/store.d.ts +47 -2
  201. package/dist/store.d.ts.map +1 -1
  202. package/dist/store.js +176 -13
  203. package/dist/store.js.map +1 -1
  204. package/dist/sync/connection.d.ts +24 -5
  205. package/dist/sync/connection.d.ts.map +1 -1
  206. package/dist/sync/connection.js +168 -31
  207. package/dist/sync/connection.js.map +1 -1
  208. package/dist/sync/engine.d.ts +97 -0
  209. package/dist/sync/engine.d.ts.map +1 -1
  210. package/dist/sync/engine.js +159 -1
  211. package/dist/sync/engine.js.map +1 -1
  212. package/dist/sync/google-calendar.d.ts.map +1 -1
  213. package/dist/sync/google-calendar.js +18 -1
  214. package/dist/sync/google-calendar.js.map +1 -1
  215. package/dist/sync/guest-words.d.ts +9 -0
  216. package/dist/sync/guest-words.d.ts.map +1 -0
  217. package/dist/sync/guest-words.js +41 -0
  218. package/dist/sync/guest-words.js.map +1 -0
  219. package/dist/sync/index.d.ts +8 -5
  220. package/dist/sync/index.d.ts.map +1 -1
  221. package/dist/sync/index.js +2 -0
  222. package/dist/sync/index.js.map +1 -1
  223. package/dist/sync/notion-stub.d.ts +4 -1
  224. package/dist/sync/notion-stub.d.ts.map +1 -1
  225. package/dist/sync/notion-stub.js +40 -0
  226. package/dist/sync/notion-stub.js.map +1 -1
  227. package/dist/sync/notion.d.ts +44 -2
  228. package/dist/sync/notion.d.ts.map +1 -1
  229. package/dist/sync/notion.js +289 -143
  230. package/dist/sync/notion.js.map +1 -1
  231. package/dist/sync/status.d.ts +43 -0
  232. package/dist/sync/status.d.ts.map +1 -0
  233. package/dist/sync/status.js +46 -0
  234. package/dist/sync/status.js.map +1 -0
  235. package/dist/sync/types.d.ts +60 -0
  236. package/dist/sync/types.d.ts.map +1 -1
  237. package/dist/sync/types.js.map +1 -1
  238. package/dist/theme/marks.d.ts +12 -0
  239. package/dist/theme/marks.d.ts.map +1 -1
  240. package/dist/theme/marks.js +5 -0
  241. package/dist/theme/marks.js.map +1 -1
  242. package/dist/tidying.d.ts +53 -4
  243. package/dist/tidying.d.ts.map +1 -1
  244. package/dist/tidying.js +110 -8
  245. package/dist/tidying.js.map +1 -1
  246. package/dist/validate-graph.d.ts +2 -1
  247. package/dist/validate-graph.d.ts.map +1 -1
  248. package/dist/validate-graph.js +32 -1
  249. package/dist/validate-graph.js.map +1 -1
  250. package/dist/version.d.ts +1 -1
  251. package/dist/version.js +1 -1
  252. package/dist/words.d.ts +4 -1
  253. package/dist/words.d.ts.map +1 -1
  254. package/dist/words.js +4 -1
  255. package/dist/words.js.map +1 -1
  256. package/dist/workflow-shape.d.ts +223 -0
  257. package/dist/workflow-shape.d.ts.map +1 -0
  258. package/dist/workflow-shape.js +31 -0
  259. package/dist/workflow-shape.js.map +1 -0
  260. package/dist/workflows/findings.d.ts +13 -0
  261. package/dist/workflows/findings.d.ts.map +1 -0
  262. package/dist/workflows/findings.js +157 -0
  263. package/dist/workflows/findings.js.map +1 -0
  264. package/dist/workflows/generate-words.d.ts +12 -0
  265. package/dist/workflows/generate-words.d.ts.map +1 -0
  266. package/dist/workflows/generate-words.js +25 -0
  267. package/dist/workflows/generate-words.js.map +1 -0
  268. package/dist/workflows/generate.d.ts +59 -0
  269. package/dist/workflows/generate.d.ts.map +1 -0
  270. package/dist/workflows/generate.js +165 -0
  271. package/dist/workflows/generate.js.map +1 -0
  272. package/dist/workflows/judging.d.ts +17 -0
  273. package/dist/workflows/judging.d.ts.map +1 -0
  274. package/dist/workflows/judging.js +11 -0
  275. package/dist/workflows/judging.js.map +1 -0
  276. package/dist/workflows/next.d.ts +18 -0
  277. package/dist/workflows/next.d.ts.map +1 -0
  278. package/dist/workflows/next.js +75 -0
  279. package/dist/workflows/next.js.map +1 -0
  280. package/dist/workflows/on-the-map.d.ts +79 -0
  281. package/dist/workflows/on-the-map.d.ts.map +1 -0
  282. package/dist/workflows/on-the-map.js +96 -0
  283. package/dist/workflows/on-the-map.js.map +1 -0
  284. package/dist/workflows/policies.d.ts +39 -0
  285. package/dist/workflows/policies.d.ts.map +1 -0
  286. package/dist/workflows/policies.js +95 -0
  287. package/dist/workflows/policies.js.map +1 -0
  288. package/dist/workflows/resolve.d.ts +55 -0
  289. package/dist/workflows/resolve.d.ts.map +1 -0
  290. package/dist/workflows/resolve.js +57 -0
  291. package/dist/workflows/resolve.js.map +1 -0
  292. package/dist/workflows/schedule.d.ts +28 -0
  293. package/dist/workflows/schedule.d.ts.map +1 -0
  294. package/dist/workflows/schedule.js +160 -0
  295. package/dist/workflows/schedule.js.map +1 -0
  296. package/dist/workflows/step-act.d.ts +6 -0
  297. package/dist/workflows/step-act.d.ts.map +1 -0
  298. package/dist/workflows/step-act.js +79 -0
  299. package/dist/workflows/step-act.js.map +1 -0
  300. package/dist/workflows/step-agent.d.ts +4 -0
  301. package/dist/workflows/step-agent.d.ts.map +1 -0
  302. package/dist/workflows/step-agent.js +55 -0
  303. package/dist/workflows/step-agent.js.map +1 -0
  304. package/dist/workflows/step-archive.d.ts +8 -0
  305. package/dist/workflows/step-archive.d.ts.map +1 -0
  306. package/dist/workflows/step-archive.js +74 -0
  307. package/dist/workflows/step-archive.js.map +1 -0
  308. package/dist/workflows/step-classify.d.ts +25 -0
  309. package/dist/workflows/step-classify.d.ts.map +1 -0
  310. package/dist/workflows/step-classify.js +160 -0
  311. package/dist/workflows/step-classify.js.map +1 -0
  312. package/dist/workflows/step-generate.d.ts +7 -0
  313. package/dist/workflows/step-generate.d.ts.map +1 -0
  314. package/dist/workflows/step-generate.js +185 -0
  315. package/dist/workflows/step-generate.js.map +1 -0
  316. package/dist/workflows/steps.d.ts +75 -0
  317. package/dist/workflows/steps.d.ts.map +1 -0
  318. package/dist/workflows/steps.js +40 -0
  319. package/dist/workflows/steps.js.map +1 -0
  320. package/dist/workflows/triggered.d.ts +35 -0
  321. package/dist/workflows/triggered.d.ts.map +1 -0
  322. package/dist/workflows/triggered.js +69 -0
  323. package/dist/workflows/triggered.js.map +1 -0
  324. package/dist/workflows/values.d.ts +28 -0
  325. package/dist/workflows/values.d.ts.map +1 -0
  326. package/dist/workflows/values.js +75 -0
  327. package/dist/workflows/values.js.map +1 -0
  328. package/dist/workflows/words.d.ts +10 -0
  329. package/dist/workflows/words.d.ts.map +1 -0
  330. package/dist/workflows/words.js +30 -0
  331. package/dist/workflows/words.js.map +1 -0
  332. package/dist/workflows/world.d.ts +23 -0
  333. package/dist/workflows/world.d.ts.map +1 -0
  334. package/dist/workflows/world.js +50 -0
  335. package/dist/workflows/world.js.map +1 -0
  336. package/dist/workflows.d.ts +35 -0
  337. package/dist/workflows.d.ts.map +1 -0
  338. package/dist/workflows.js +20 -0
  339. package/dist/workflows.js.map +1 -0
  340. package/package.json +12 -2
package/dist/store.js CHANGED
@@ -4,6 +4,7 @@ import { Graph, GraphError, MissingRecordError } from "./graph/graph.js";
4
4
  import { resolveModules } from "./modules.js";
5
5
  import { diffSnapshots, diffTouched, EMPTY_DIFF, isEmptyDiff } from "./graph/diff.js";
6
6
  import { normalize, writesOf } from "./graph/primitives.js";
7
+ import { keepLinked, keepsLinked } from "./keep-linked.js";
7
8
  import { bareOp, inverseOf } from "./ops/inverse.js";
8
9
  import { LabelIndex, refusalFor } from "./labels.js";
9
10
  import { nodeRefArgs } from "./mutations/node-ref.js";
@@ -19,11 +20,12 @@ import { namesMade, resolveMade } from "./mutations/made.js";
19
20
  import { OperationLog } from "./ops/log.js";
20
21
  import { lineageOf } from "./ops/lineage.js";
21
22
  import { KEEP_LINK, keepLinkMutation, keepLinkVia } from "./mutations/keep-link.js";
22
- import { VOTE_SUGGESTION, VOTING, voteSuggestionMutation } from "./mutations/vote-suggestion.js";
23
+ import { advisoryVoterOf, signedOutSeat, SIGN_IN_TO_VOTE, VOTE_SUGGESTION, VOTING, voteSuggestionMutation } from "./mutations/vote-suggestion.js";
23
24
  import { TYPE_TEXT, TYPING } from "./text/typing-hook.js";
24
25
  import { declinedIn, isAutomated, tidyingRefusal } from "./ops/tidying.js";
25
26
  import { nameOfAuthor } from "./who.js";
26
- import { actingAs, grantsTo, isSystem, permits, permittedMutations, subjectKinds } from "./permissions/policy.js";
27
+ import { actingAs, grantsTo, isSystem, permits, permittedMutations, proposes, subjectKinds } from "./permissions/policy.js";
28
+ import { MOST_PROPOSED, PROPOSE_CHANGES, proposeChangesMutation } from "./mutations/propose-changes.js";
27
29
  import { namesUnseen, redact } from "./ops/withheld.js";
28
30
  import { writersBehind } from "./ops/writers.js";
29
31
  import { nounOf } from "./schema/define-node.js";
@@ -187,6 +189,8 @@ export class Store {
187
189
  typer;
188
190
  /** "Vote on a suggestion" (Roadmap f-suggestion-votes), when the app declared no act by that name. */
189
191
  voter;
192
+ /** "Propose changes" (Roadmap f-proposals-for-review), when the app declared no act by that name. */
193
+ proposer;
190
194
  invariants;
191
195
  invariantOptions;
192
196
  policy;
@@ -194,6 +198,8 @@ export class Store {
194
198
  publishing;
195
199
  /** What the declaration says of suggestions as a whole (`StoreOptions.tidying`). */
196
200
  tidying;
201
+ /** What the host says of votes from a shared or signed-out seat (`StoreOptions.votes`). */
202
+ votes;
197
203
  /**
198
204
  * The intelligence the app declared, so a surface can read the DOORS.
199
205
  *
@@ -230,6 +236,8 @@ export class Store {
230
236
  rehearsing = false;
231
237
  /** True of a rehearsal for a suggestion: the actor is held to what a person said, not to the declaration's `tidying`, since a person decides it. */
232
238
  suggesting = false;
239
+ /** True of a rehearsal for a proposal (Roadmap f-proposals-for-review): an act its author may propose is let through, as it will be for whoever accepts it. */
240
+ proposing = false;
233
241
  /**
234
242
  * The day a host last ticked this store to (FR-176), and the day the clock
235
243
  * read when it did: what its rules are judged on, where nothing pins one,
@@ -257,6 +265,7 @@ export class Store {
257
265
  this.policy = options.policy;
258
266
  this.publishing = options.publishing;
259
267
  this.tidying = options.tidying;
268
+ this.votes = options.votes;
260
269
  this.by = options.principal ?? HUMAN;
261
270
  this.intelligence = options.intelligence ?? [];
262
271
  for (const provider of options.intelligence ?? []) {
@@ -299,6 +308,11 @@ export class Store {
299
308
  this.voter = voteSuggestionMutation();
300
309
  this.mutations.set(VOTE_SUGGESTION, this.voter);
301
310
  }
311
+ // "Propose changes" (Roadmap f-proposals-for-review): answered by every store, offered by no list.
312
+ if (!options.past && !this.mutations.has(PROPOSE_CHANGES)) {
313
+ this.proposer = proposeChangesMutation();
314
+ this.mutations.set(PROPOSE_CHANGES, this.proposer);
315
+ }
302
316
  const horizon = given("horizon", options.horizon);
303
317
  const validate = { validate: this.validate };
304
318
  if (options.log) {
@@ -507,7 +521,7 @@ export class Store {
507
521
  return mutation.derived !== undefined && this.modules.disabledKinds.has(mutation.derived.kind);
508
522
  }
509
523
  allMutations() {
510
- return [...this.mutations.values(), ...(this.publisher ? [this.publisher] : [])].filter((mutation) => mutation !== this.keeper && mutation !== this.typer && mutation !== this.voter && !this.mutationDisabled(mutation));
524
+ return [...this.mutations.values(), ...(this.publisher ? [this.publisher] : [])].filter((mutation) => mutation !== this.keeper && mutation !== this.typer && mutation !== this.voter && mutation !== this.proposer && !this.mutationDisabled(mutation));
511
525
  }
512
526
  /** The declared acts a derived act resolves its permission through; keeping a link, those that make or break its relation. */
513
527
  viaOf(mutation, call, principal) {
@@ -560,6 +574,23 @@ export class Store {
560
574
  * action withheld by permission is stated rather than hidden, and stating
561
575
  * it needs the same answer the enforcement uses. One source, two readings.
562
576
  */
577
+ /**
578
+ * Whether a change to a derived record may go back to its source
579
+ * (Roadmap f-keep-linked): when the store judges a policy, only when its
580
+ * author may make the source's own edit with that value.
581
+ */
582
+ writesBack(principal) {
583
+ if (!this.policy)
584
+ return undefined;
585
+ return (source, field, value) => {
586
+ try {
587
+ return this.permits({ name: `edit-${source.kind}`, args: { id: source.id, [field]: value } }, principal).ok;
588
+ }
589
+ catch {
590
+ return false;
591
+ }
592
+ };
593
+ }
563
594
  permits(call, principal = HUMAN) {
564
595
  call = this.spelledCall(call);
565
596
  /*
@@ -578,9 +609,20 @@ export class Store {
578
609
  // Publishing is the declaration's `publishers`, not a grant's.
579
610
  if (mutation && mutation === this.publisher)
580
611
  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)
612
+ /*
613
+ * 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.
614
+ * But not a seat that is nobody in particular (Roadmap f-votes-shared-seat): every signed-out stranger served as one `anonymous`
615
+ * cast one shared vote that any of them could flip. Refused, unless the host keeps such votes as advisory and said whose this is.
616
+ */
617
+ if (mutation && mutation === this.voter) {
618
+ if (signedOutSeat(principal) && (this.votes?.advisory !== "visitors" || advisoryVoterOf(principal) === undefined)) {
619
+ return { ok: false, refusal: { mutation: call.name, wouldNeed: [], message: SIGN_IN_TO_VOTE } };
620
+ }
583
621
  return { ok: true };
622
+ }
623
+ // A proposal is its author's to make where each call it holds is one they may run or may propose (Roadmap f-proposals-for-review).
624
+ if (mutation && mutation === this.proposer)
625
+ return this.permitsProposal(call, principal);
584
626
  /*
585
627
  * KEEPING A LINK ONLY THE DERIVED ACTS MAKE is judged as they are: on
586
628
  * the record it leaves, so riding everybody's grant it reaches only the
@@ -635,6 +677,82 @@ export class Store {
635
677
  */
636
678
  return permits(this.policy, principal, call.name, kind, removeVia([...this.mutations.values()], mutation.derived.kind), typeof id === "string" ? id : undefined, this.words);
637
679
  }
680
+ /**
681
+ * WHETHER THIS PRINCIPAL MAY PROPOSE THIS CALL AND MAY NOT RUN IT (Roadmap
682
+ * f-proposals-for-review): the policy refuses them the act and a grant's
683
+ * `propose` names it, on the kind the call is on. What turns an act's
684
+ * form's button into "Propose", and what a proposal's rehearsal lets
685
+ * through.
686
+ */
687
+ mayPropose(call, principal = HUMAN) {
688
+ if (!this.policy || this.past || signedOutSeat(principal))
689
+ return false;
690
+ call = this.spelledCall(call);
691
+ const mutation = this.mutations.get(call.name);
692
+ if (!mutation || mutation === this.proposer || mutation === this.voter || this.refusesAgent(call, principal) || this.namesTurnedOff(call, principal))
693
+ return false;
694
+ return !this.permits(call, principal).ok && proposes(this.policy, principal, call.name, this.subjectKindOf(call, principal));
695
+ }
696
+ /**
697
+ * A PROPOSAL'S OP (Roadmap f-proposals-for-review): the calls a "Propose
698
+ * changes" call holds, rehearsed as its author with the grant to propose
699
+ * standing in for the grant to run, kept as `suggest` keeps a suggestion
700
+ * — no primitives, each call's sentence, the records they name — with
701
+ * the author as its maker and a lineage naming them (`proposedBy`), its
702
+ * basis the proposal in their own words, else "Proposed by Ada"; or
703
+ * undefined when the calls would change nothing. Made here, so a store
704
+ * answers a proposal over the wire whatever else it loaded.
705
+ */
706
+ proposal(call, batch, author, via) {
707
+ const { calls, intent } = call.args;
708
+ if (calls.length === 0 || calls.length > MOST_PROPOSED || calls.some((one) => !one.args || typeof one.args !== "object" || Array.isArray(one.args)))
709
+ throw new GraphError(`A proposal holds 1 to ${MOST_PROPOSED} changes, each an act's name and its arguments.`);
710
+ const by = actingAs(author).id ?? author.session;
711
+ if (by === undefined)
712
+ throw new GraphError("A proposal is somebody's: sign in to propose a change.");
713
+ const lineage = lineageOf({ basis: (intent ?? `Proposed by ${author.name ?? by}`).slice(0, 500), proposedBy: by.slice(0, 120) });
714
+ const rehearsal = this.rehearsal();
715
+ rehearsal.proposing = true;
716
+ const planned = rehearsal.applying(calls, { author, lineage, ...given("via", via) });
717
+ if (planned.ops.length === 0)
718
+ return undefined;
719
+ const described = [...new Set(planned.ops.flatMap((op) => op.described ?? []))];
720
+ const own = planned.ops.filter((one) => one.mutation !== null && one.author === author);
721
+ return {
722
+ id: this.nextId(),
723
+ seq: this.log.length,
724
+ batch,
725
+ author,
726
+ intent: planned.ops.map((one) => one.intent).join("; "),
727
+ ...given("batchIntent", intent),
728
+ mutation: null,
729
+ primitives: [],
730
+ reads: [],
731
+ writes: [],
732
+ ...(described.length > 0 ? { described } : {}),
733
+ at: this.now(),
734
+ ...given("via", via),
735
+ lineage,
736
+ suggests: calls,
737
+ ...given("suggestsSaid", calls.length > 1 && own.length === calls.length ? own.map((one) => one.intent) : undefined),
738
+ };
739
+ }
740
+ /** A "Propose changes" call, judged: every call it holds one this principal may run or may propose, from a seat that is somebody. */
741
+ permitsProposal(call, principal) {
742
+ const no = (message) => ({ ok: false, refusal: { mutation: call.name, wouldNeed: [], message } });
743
+ if (signedOutSeat(principal))
744
+ return no("Sign in to propose a change.");
745
+ const calls = Array.isArray(call.args["calls"]) ? call.args["calls"] : [];
746
+ for (const one of calls) {
747
+ if (typeof one?.name !== "string")
748
+ continue;
749
+ const verdict = this.permits(one, principal);
750
+ if (verdict.ok || this.mayPropose(one, principal))
751
+ continue;
752
+ return no(`Not permitted to propose this: ${verdict.refusal.message.replace(/^Not permitted: /, "")}`);
753
+ }
754
+ return { ok: true };
755
+ }
638
756
  /**
639
757
  * The first record linked to the subject that this principal may not
640
758
  * change, as a refusal of the act naming it (Roadmap f-acts-update-linked);
@@ -1123,7 +1241,15 @@ export class Store {
1123
1241
  call = this.spelledCall(call);
1124
1242
  const definition = this.mutation(call.name);
1125
1243
  const compiled = compileMutation(this.graph, definition, call.args, this.sighted(options.author));
1126
- return this.previewPrimitives(this.asSplices(compiled.primitives), {
1244
+ const made = this.asSplices(compiled.primitives);
1245
+ // And what would follow it (Roadmap f-keep-linked), worked out on a copy.
1246
+ let following = [];
1247
+ if (keepsLinked(this.schema)) {
1248
+ const trial = Graph.from(this.schema, this.graph.snapshot());
1249
+ trial.applyPrimitives(made);
1250
+ following = keepLinked(this.schema, trial, made, { mayWriteBack: this.writesBack(options.author ?? this.by) });
1251
+ }
1252
+ return this.previewPrimitives([...made, ...following], {
1127
1253
  reads: compiled.reads,
1128
1254
  writes: compiled.writes,
1129
1255
  intent: compiled.intent,
@@ -1261,8 +1387,11 @@ export class Store {
1261
1387
  * was brought in for, and the narrower of the two wins.
1262
1388
  */
1263
1389
  const permitted = (call, as = author) => {
1264
- const verdict = this.policy || (this.publisher && call.name === PUBLISH) ? this.permits(call, as) : undefined;
1265
- if (verdict && !verdict.ok)
1390
+ // A vote is judged with no policy too: a shared seat's vote is refused whether or not the app says who may do what (Roadmap f-votes-shared-seat).
1391
+ const voting = this.voter !== undefined && this.mutations.get(call.name) === this.voter;
1392
+ const verdict = this.policy || (this.publisher && call.name === PUBLISH) || voting || this.mutations.get(call.name) === this.proposer ? this.permits(call, as) : undefined;
1393
+ // Rehearsing a proposal, an act its author may propose stands as the reviewer's would (Roadmap f-proposals-for-review).
1394
+ if (verdict && !verdict.ok && !(this.proposing && this.mayPropose(call, as)))
1266
1395
  this.refuse(verdict.refusal, as.id);
1267
1396
  };
1268
1397
  const declared = (call, as = author) => {
@@ -1337,6 +1466,16 @@ export class Store {
1337
1466
  queue.push({ sent: made, as: accept.author, lineage: lineageOf(accept.lineage), decided: true });
1338
1467
  continue;
1339
1468
  }
1469
+ if (definition === this.proposer) {
1470
+ // A proposal (Roadmap f-proposals-for-review): a suggestion with its author as its maker, and nothing of the graph until it is accepted.
1471
+ const op = this.proposal(call, batch, as, options.via);
1472
+ if (!op)
1473
+ continue;
1474
+ this.log.append(op);
1475
+ ops.push(op);
1476
+ intents.push(op.intent);
1477
+ continue;
1478
+ }
1340
1479
  const missing = this.missingFor(call, as);
1341
1480
  if (missing)
1342
1481
  throw missing;
@@ -1371,6 +1510,15 @@ export class Store {
1371
1510
  if (compiled.primitives.length === 0)
1372
1511
  continue;
1373
1512
  minted[at] = compiled.firstMinted;
1513
+ /*
1514
+ * WHAT FOLLOWS IT (Roadmap f-keep-linked): the records that derive
1515
+ * from what this changed take their values from it, and a change
1516
+ * to one of those goes back to its source — in this op, so one undo
1517
+ * takes both back. Applied to the graph as they are worked out.
1518
+ */
1519
+ const made = this.asSplices(compiled.primitives, options.base).map(normalize);
1520
+ this.graph.applyPrimitives(made);
1521
+ const following = keepsLinked(this.schema) ? keepLinked(this.schema, this.graph, made, { mayWriteBack: this.writesBack(as) }).map(normalize) : [];
1374
1522
  madeIds[at] = compiled.primitives.find((primitive) => primitive.op === "add-node")?.node.id;
1375
1523
  const op = {
1376
1524
  id: this.nextId(),
@@ -1392,9 +1540,9 @@ export class Store {
1392
1540
  * instruction is lost the moment the op is written to JSON and the
1393
1541
  * inverse quietly does nothing. See `normalize` in primitives.ts.
1394
1542
  */
1395
- primitives: this.asSplices(compiled.primitives, options.base).map(normalize),
1543
+ primitives: [...made, ...following],
1396
1544
  reads: compiled.reads,
1397
- writes: compiled.writes,
1545
+ writes: following.length > 0 ? [...new Set([...compiled.writes, ...following.flatMap(writesOf)])] : compiled.writes,
1398
1546
  // Which records its sentence read, so a reader who may not see one is not handed the sentence (FR-55).
1399
1547
  ...(definition.describe !== undefined && compiled.described && compiled.described.length > 0 ? { described: compiled.described } : {}),
1400
1548
  ...given("mergedInto", compiled.mergedInto),
@@ -1402,7 +1550,6 @@ export class Store {
1402
1550
  ...given("via", options.via),
1403
1551
  ...given("lineage", lined),
1404
1552
  };
1405
- this.graph.applyPrimitives(op.primitives);
1406
1553
  this.log.append(op);
1407
1554
  ops.push(op);
1408
1555
  allPrimitives.push(...op.primitives);
@@ -1549,6 +1696,8 @@ export class Store {
1549
1696
  ...given("undoes", made.undoes),
1550
1697
  ...given("via", made.via),
1551
1698
  ...given("lineage", lineageOf(made.lineage)),
1699
+ ...given("described", made.described),
1700
+ ...given("sentOut", made.sentOut),
1552
1701
  };
1553
1702
  if (taken.has(op.id))
1554
1703
  throw new GraphError(`Op "${op.id}" (${op.intent}) is already in the log`, "A host-made op needs an id of its own; leave `id` out and the store mints one.");
@@ -1592,7 +1741,18 @@ export class Store {
1592
1741
  const rollback = this.graph.snapshot();
1593
1742
  const intent = options.intent ?? "Apply a change";
1594
1743
  const lineage = lineageOf(options.lineage);
1595
- const recorded = this.asSplices(primitives, options.base).map(normalize);
1744
+ const handed = this.asSplices(primitives, options.base).map(normalize);
1745
+ // What follows it (Roadmap f-keep-linked), worked out on the graph as it lands: the host's change keeps derived records in step too.
1746
+ this.graph.applyPrimitives(handed);
1747
+ let recorded = handed;
1748
+ try {
1749
+ if (keepsLinked(this.schema))
1750
+ recorded = [...handed, ...keepLinked(this.schema, this.graph, handed, { mayWriteBack: this.writesBack(author) }).map(normalize)];
1751
+ }
1752
+ catch (error) {
1753
+ this.putBack(rollback, this.log.length);
1754
+ throw error;
1755
+ }
1596
1756
  const ops = [];
1597
1757
  if (recorded.length > 0) {
1598
1758
  const op = {
@@ -1609,7 +1769,6 @@ export class Store {
1609
1769
  ...given("via", options.via),
1610
1770
  ...given("lineage", lineage),
1611
1771
  };
1612
- this.graph.applyPrimitives(op.primitives);
1613
1772
  this.log.append(op);
1614
1773
  ops.push(op);
1615
1774
  tellTheWatchOfAnAuthor(author.id);
@@ -1789,6 +1948,10 @@ export class Store {
1789
1948
  const by = actingAs(judged.author).id;
1790
1949
  // 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
1950
  if (judged.vote !== undefined) {
1951
+ // An advisory vote is its visitor's, not the shared seat's (Roadmap f-votes-shared-seat): another stranger on the same seat may not take it back.
1952
+ if (judged.vote.advisory && !isSystem(author) && advisoryVoterOf(author) !== judged.vote.voter && !(this.policy && this.grantsEveryAct(author))) {
1953
+ return { mutation: "undo", wouldNeed: [], message: `Not permitted to undo "${target.intent}": only whoever voted takes their vote back.` };
1954
+ }
1792
1955
  if (this.policy && !isSystem(author) && by !== actingAs(author).id && !this.grantsEveryAct(author)) {
1793
1956
  return { mutation: "undo", wouldNeed: [], message: `Not permitted to undo "${target.intent}": only whoever voted takes their vote back.` };
1794
1957
  }