visualproof 0.0.0-stage → 0.2.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 (146) hide show
  1. package/LICENSE +202 -0
  2. package/README.md +99 -2
  3. package/dist/assets/fonts/JuliaMono-Regular.ttf +0 -0
  4. package/dist/assets/fonts/JuliaMono-RegularItalic.ttf +0 -0
  5. package/dist/assets/fonts/JuliaMono-SemiBold.ttf +0 -0
  6. package/dist/assets/fonts/LICENSE-JuliaMono.txt +93 -0
  7. package/dist/assets/latex/visualproof.sty +308 -0
  8. package/dist/assets/webfonts/LICENSE-Jost.txt +93 -0
  9. package/dist/assets/webfonts/LICENSE-SourceSans3.txt +93 -0
  10. package/dist/assets/webfonts/LICENSE-SourceSerif4.txt +93 -0
  11. package/dist/assets/webfonts/jost-latin-ext-wght-normal.woff2 +0 -0
  12. package/dist/assets/webfonts/jost-latin-wght-normal.woff2 +0 -0
  13. package/dist/assets/webfonts/source-sans-3-greek-wght-italic.woff2 +0 -0
  14. package/dist/assets/webfonts/source-sans-3-greek-wght-normal.woff2 +0 -0
  15. package/dist/assets/webfonts/source-sans-3-latin-ext-wght-italic.woff2 +0 -0
  16. package/dist/assets/webfonts/source-sans-3-latin-ext-wght-normal.woff2 +0 -0
  17. package/dist/assets/webfonts/source-sans-3-latin-wght-italic.woff2 +0 -0
  18. package/dist/assets/webfonts/source-sans-3-latin-wght-normal.woff2 +0 -0
  19. package/dist/assets/webfonts/source-serif-4-greek-wght-italic.woff2 +0 -0
  20. package/dist/assets/webfonts/source-serif-4-greek-wght-normal.woff2 +0 -0
  21. package/dist/assets/webfonts/source-serif-4-latin-ext-wght-italic.woff2 +0 -0
  22. package/dist/assets/webfonts/source-serif-4-latin-ext-wght-normal.woff2 +0 -0
  23. package/dist/assets/webfonts/source-serif-4-latin-wght-italic.woff2 +0 -0
  24. package/dist/assets/webfonts/source-serif-4-latin-wght-normal.woff2 +0 -0
  25. package/dist/extractor/Extract.lean +1180 -0
  26. package/dist/extractor/Usage.lean +900 -0
  27. package/dist/mcp/visualproof-mcp.mjs +476 -0
  28. package/dist/server/access.js +95 -0
  29. package/dist/server/agent.js +3270 -0
  30. package/dist/server/agentdocs.js +35 -0
  31. package/dist/server/agentlibrary.js +202 -0
  32. package/dist/server/agentnotes.js +701 -0
  33. package/dist/server/agentviews.js +703 -0
  34. package/dist/server/askfollow.js +286 -0
  35. package/dist/server/boardspots.js +159 -0
  36. package/dist/server/dirs.js +175 -0
  37. package/dist/server/doctor.js +186 -0
  38. package/dist/server/events.js +198 -0
  39. package/dist/server/export.js +420 -0
  40. package/dist/server/extract.js +204 -0
  41. package/dist/server/fetchurl.js +321 -0
  42. package/dist/server/figures.js +158 -0
  43. package/dist/server/history.js +780 -0
  44. package/dist/server/historyapi.js +304 -0
  45. package/dist/server/hover.js +595 -0
  46. package/dist/server/identity.js +114 -0
  47. package/dist/server/invites.js +241 -0
  48. package/dist/server/layoutthread.js +51 -0
  49. package/dist/server/library.js +600 -0
  50. package/dist/server/libraryroute.js +211 -0
  51. package/dist/server/live.js +1101 -0
  52. package/dist/server/lostfound.js +199 -0
  53. package/dist/server/main.js +1406 -0
  54. package/dist/server/mcptools.js +496 -0
  55. package/dist/server/pastdoc.js +73 -0
  56. package/dist/server/pipeline.js +159 -0
  57. package/dist/server/poppler.js +251 -0
  58. package/dist/server/presence.js +252 -0
  59. package/dist/server/project.js +222 -0
  60. package/dist/server/prompt.js +53 -0
  61. package/dist/server/readonly.js +152 -0
  62. package/dist/server/readonlygate.js +123 -0
  63. package/dist/server/renames.js +268 -0
  64. package/dist/server/review.js +334 -0
  65. package/dist/server/reviewapi.js +188 -0
  66. package/dist/server/snapshot.js +50 -0
  67. package/dist/server/sourcestore.js +473 -0
  68. package/dist/server/status.js +360 -0
  69. package/dist/server/talk.js +476 -0
  70. package/dist/server/tokens.js +221 -0
  71. package/dist/server/usage.js +501 -0
  72. package/dist/server/versions.js +82 -0
  73. package/dist/server/view.js +164 -0
  74. package/dist/server/views.js +1167 -0
  75. package/dist/shared/askfollow.js +46 -0
  76. package/dist/shared/board.js +332 -0
  77. package/dist/shared/build.js +2 -0
  78. package/dist/shared/collab.js +241 -0
  79. package/dist/shared/comment.js +285 -0
  80. package/dist/shared/context.js +157 -0
  81. package/dist/shared/doc/authors.js +98 -0
  82. package/dist/shared/doc/build.js +358 -0
  83. package/dist/shared/doc/latex.js +455 -0
  84. package/dist/shared/doc/markdown.js +159 -0
  85. package/dist/shared/doc/model.js +37 -0
  86. package/dist/shared/doc/order.js +124 -0
  87. package/dist/shared/doc/outline.js +45 -0
  88. package/dist/shared/doc/proof.js +272 -0
  89. package/dist/shared/doc/sections.js +93 -0
  90. package/dist/shared/doc/settings.js +75 -0
  91. package/dist/shared/edgecodec.js +102 -0
  92. package/dist/shared/editor.js +64 -0
  93. package/dist/shared/excerpt.js +87 -0
  94. package/dist/shared/graph.js +16 -0
  95. package/dist/shared/items.js +92 -0
  96. package/dist/shared/keys.js +203 -0
  97. package/dist/shared/layout.js +926 -0
  98. package/dist/shared/lexer.js +130 -0
  99. package/dist/shared/library.js +711 -0
  100. package/dist/shared/lostfound.js +150 -0
  101. package/dist/shared/moves.js +244 -0
  102. package/dist/shared/notecard.js +295 -0
  103. package/dist/shared/noteplace.js +505 -0
  104. package/dist/shared/palette.js +46 -0
  105. package/dist/shared/pastgraph.js +103 -0
  106. package/dist/shared/places.js +80 -0
  107. package/dist/shared/projectnotes.js +244 -0
  108. package/dist/shared/query.js +818 -0
  109. package/dist/shared/review.js +642 -0
  110. package/dist/shared/reviewqueue.js +333 -0
  111. package/dist/shared/ring.js +366 -0
  112. package/dist/shared/scope.js +255 -0
  113. package/dist/shared/scopeedit.js +81 -0
  114. package/dist/shared/search.js +191 -0
  115. package/dist/shared/separate.js +178 -0
  116. package/dist/shared/sharesnap.js +281 -0
  117. package/dist/shared/sticky.js +501 -0
  118. package/dist/shared/timeline.js +73 -0
  119. package/dist/shared/titles.js +300 -0
  120. package/dist/shared/viewgraph.js +157 -0
  121. package/dist/shared/views.js +859 -0
  122. package/dist/web/bundle/app.js +52671 -0
  123. package/dist/web/bundle/edges.js +407 -0
  124. package/dist/web/bundle/layout.js +1010 -0
  125. package/dist/web/bundle/pdfworker.js +48322 -0
  126. package/dist/web/index.html +257 -0
  127. package/dist/web/src/motion.js +430 -0
  128. package/dist/web/src/past.js +224 -0
  129. package/dist/web/src/scene.js +804 -0
  130. package/dist/web/style.css +2548 -0
  131. package/docs/agent-api/README.md +91 -0
  132. package/docs/agent-api/advice.md +108 -0
  133. package/docs/agent-api/export.md +120 -0
  134. package/docs/agent-api/following.md +140 -0
  135. package/docs/agent-api/history.md +142 -0
  136. package/docs/agent-api/library.md +65 -0
  137. package/docs/agent-api/mcp.md +104 -0
  138. package/docs/agent-api/notes.md +93 -0
  139. package/docs/agent-api/reading.md +166 -0
  140. package/docs/agent-api/requests.md +94 -0
  141. package/docs/agent-api/talking.md +100 -0
  142. package/docs/agent-api/tcb.md +135 -0
  143. package/docs/agent-api/views.md +125 -0
  144. package/docs/agent-api/words.md +56 -0
  145. package/package.json +39 -4
  146. package/test/toolchains.json +5 -0
@@ -0,0 +1,333 @@
1
+ // The order in which to review a target's TCB and why a declaration is in the TCB at all
2
+ // (ReviewSpec, "Reviewing a target"): pure functions of the graph and the review. The page's review
3
+ // (web/src/reviewqueue.ts: the strip, the target's review list), the agent API (server/agent.ts) and
4
+ // the server's checks of key declarations (server/reviewapi.ts) walk a TCB the same way.
5
+ //
6
+ // Where the review works is a view's focus (ViewSpec decision 28; shared/views.ts, viewFocus), not
7
+ // what its scope says; whatever the board holds, the review counts the focus's whole review set.
8
+ //
9
+ // What a target's review holds (decided 2026-10-08) is the scope `reviewScope` (shared/query.ts)
10
+ // evaluated on the graph — the TCB without what is trusted, an untrusted outside declaration
11
+ // included, and every non-standard axiom the target rests on through statements, bodies or proofs,
12
+ // which no prefix trusts. The queue and its progress count exactly that set, so that a board of that
13
+ // scope and the numbers beside it never disagree. An axiom the graph has no declaration for (one only
14
+ // the axiom sets name, used inside something outside the project) cannot be on a board: it is
15
+ // counted beside the queue, and approved from the target's panel.
16
+ import { SITE_BODY, SITE_STMT } from "./graph.js";
17
+ import { current, fresh, STANDARD_AXIOMS, statusOf, trusted } from "./review.js";
18
+ import { ALL_SITES } from "./viewgraph.js";
19
+ import { evaluate, reviewScope } from "./query.js";
20
+ /** The references a TCB follows: in statements and bodies, never inside a proof. */
21
+ export const TCB_SITES = SITE_STMT | SITE_BODY;
22
+ /** In how many of the targets' TCBs each declaration is. */
23
+ export function inTargets(g, targets) {
24
+ const out = new Uint16Array(g.n);
25
+ for (const t of targets) {
26
+ const tcb = g.reach([t], TCB_SITES, "out");
27
+ for (let i = 0; i < g.n; i++)
28
+ out[i] += tcb[i];
29
+ }
30
+ return out;
31
+ }
32
+ /** Whether declaration `i` is an axiom beyond Lean's three standard ones: it needs review whatever
33
+ * is trusted (ReviewSpec decision 3). */
34
+ export function nonStandardAxiom(g, i) {
35
+ return g.nodes[i].kind === "axiom" && !STANDARD_AXIOMS.includes(g.nodes[i].name);
36
+ }
37
+ /** Whether declaration `i` needs review where it is in a target's TCB: no prefix trusts it, or it
38
+ * is a non-standard axiom. */
39
+ export function needsReview(g, d, i) {
40
+ return nonStandardAxiom(g, i) || !trusted(d, g.nodes[i].name, g.nodes[i].module, g.isExt(i));
41
+ }
42
+ /** What `root`'s review holds, its review set (`reviewScope`), in the graph's numbering. */
43
+ export function reviewSet(g, d, root) {
44
+ return evaluate(reviewScope(g.nodes[root].name), { g, review: d });
45
+ }
46
+ const VIA = 8;
47
+ /** The non-standard axioms `root` rests on, through statements, bodies or proofs: those the axiom
48
+ * sets of what it reaches name (the extractor's, through everything), and those it reaches as
49
+ * declarations. `set` is its review's set (reviewSet). */
50
+ export function axiomsReached(g, root, set) {
51
+ const meta = g.meta;
52
+ const reach = g.reach([root], ALL_SITES, "out");
53
+ const names = new Set();
54
+ for (let i = 0; i < g.n; i++) {
55
+ if (!reach[i])
56
+ continue;
57
+ if (nonStandardAxiom(g, i))
58
+ names.add(g.nodes[i].name);
59
+ for (const k of meta.axiomSets[g.nodes[i].ax] ?? [])
60
+ if (!STANDARD_AXIOMS.includes(meta.axioms[k]))
61
+ names.add(meta.axioms[k]);
62
+ }
63
+ const out = [];
64
+ for (const name of [...names].sort()) {
65
+ const j = g.byName.get(name);
66
+ const node = j !== undefined && set[j] ? j : null;
67
+ const via = [];
68
+ if (node !== null) {
69
+ for (let e = g.inStart[node]; e < g.inStart[node + 1]; e++)
70
+ if (reach[g.inSrc[e]] && g.inSrc[e] !== node && !via.includes(g.inSrc[e]))
71
+ via.push(g.inSrc[e]);
72
+ }
73
+ else {
74
+ const k = meta.axioms.indexOf(name);
75
+ const rests = (i) => k >= 0 && (meta.axiomSets[g.nodes[i].ax] ?? []).includes(k);
76
+ for (let i = 0; i < g.n; i++) {
77
+ if (!reach[i] || !rests(i))
78
+ continue;
79
+ let deeper = false;
80
+ for (let e = g.outStart[i]; e < g.outStart[i + 1] && !deeper; e++)
81
+ deeper = g.outDst[e] !== i && rests(g.outDst[e]);
82
+ if (!deeper)
83
+ via.push(i);
84
+ }
85
+ }
86
+ // the project's own first: that is where someone can do something about it
87
+ via.sort((a, b) => Number(g.isExt(a)) - Number(g.isExt(b)) || (g.nodes[a].name < g.nodes[b].name ? -1 : 1));
88
+ out.push({ name, node, via: via.slice(0, VIA) });
89
+ }
90
+ return out;
91
+ }
92
+ /** `root`'s review queue. `shared`: in how many targets' TCBs each declaration is (inTargets). */
93
+ export function reviewQueue(g, d, root, shared) {
94
+ const set = reviewSet(g, d, root);
95
+ const order = reviewOrder(g, set, shared);
96
+ const ax = order.filter((i) => g.nodes[i].kind === "axiom");
97
+ return { order: [...ax, ...order.filter((i) => g.nodes[i].kind !== "axiom")], axioms: axiomsReached(g, root, set) };
98
+ }
99
+ /** The axioms of a queue the graph has no declaration for on the board. */
100
+ export function offBoard(q) {
101
+ return q.axioms.filter((a) => a.node === null);
102
+ }
103
+ /** How far a target's review has come: every member of its queue, the axioms beside it included. */
104
+ export function queueProgress(g, d, q) {
105
+ const p = { total: 0, approved: 0, commented: 0, stale: 0 };
106
+ const count = (name, hash) => {
107
+ p.total++;
108
+ const s = statusOf(d, name, hash);
109
+ if (s === "approved")
110
+ p.approved++;
111
+ else if (s === "commented")
112
+ p.commented++;
113
+ else if (s === "stale")
114
+ p.stale++;
115
+ };
116
+ for (const i of q.order)
117
+ count(g.nodes[i].name, g.nodes[i].hash);
118
+ for (const a of offBoard(q))
119
+ count(a.name, undefined);
120
+ return p;
121
+ }
122
+ /** How many members `root`'s review counts under the review `d` (what trusting a prefix would
123
+ * change, web/src/trust.ts): its review set, and the axioms beside it. */
124
+ export function reviewCount(g, d, root) {
125
+ const set = reviewSet(g, d, root);
126
+ let n = 0;
127
+ for (let i = 0; i < g.n; i++)
128
+ n += set[i];
129
+ return n + axiomsReached(g, root, set).filter((a) => a.node === null).length;
130
+ }
131
+ /** Whether a reader is done with a member of a review: they gave an opinion on its present
132
+ * version — and, for a non-standard axiom, it is approved, so that "All reviewed" is never said
133
+ * while one is not. */
134
+ export function settled(d, reader, name, hash, axiom) {
135
+ return reviewedBy(d, reader, name, hash) && (!axiom || statusOf(d, name, hash) === "approved");
136
+ }
137
+ /** The declarations of `members` (a target's review, reviewSet) in the order to review them:
138
+ * bottom-up, what a declaration uses before it, since reviewing one means "it says what it
139
+ * should, given that what it uses means what it says" (ReviewSpec decision 2). Declarations that
140
+ * use each other are one step, together. Where the order leaves a choice, what more targets rest
141
+ * on comes first (`shared`, per declaration: one review of a common foundation advances every
142
+ * target on it), then the name. */
143
+ export function reviewOrder(g, set, shared) {
144
+ const members = [];
145
+ for (let i = 0; i < g.n; i++)
146
+ if (set[i])
147
+ members.push(i);
148
+ return dependencyOrder(g, members, (x, y) => (shared[x] !== shared[y] ? shared[x] > shared[y] : g.nodes[x].name < g.nodes[y].name));
149
+ }
150
+ /** Declarations `members` of `g` in dependency order, over statement and body references among
151
+ * them: what a declaration uses before it, declarations that use each other together, one step;
152
+ * where the order leaves a choice, `before` decides (by default, the name). The review queue's
153
+ * order (reviewOrder). */
154
+ export function dependencyOrder(g, members, before = (x, y) => g.nodes[x].name < g.nodes[y].name) {
155
+ const local = new Map();
156
+ members.forEach((v, k) => local.set(v, k));
157
+ // what each member uses among the members; nothing outside the project uses the project, so no
158
+ // path between two members of a review leaves them
159
+ const uses = members.map(() => []);
160
+ members.forEach((u, a) => {
161
+ for (let e = g.outStart[u]; e < g.outStart[u + 1]; e++) {
162
+ const b = g.outSite[e] & TCB_SITES ? local.get(g.outDst[e]) : undefined;
163
+ if (b !== undefined && b !== a)
164
+ uses[a].push(b);
165
+ }
166
+ });
167
+ return stepOrder(uses, (a, b) => before(members[a], members[b])).map((a) => members[a]);
168
+ }
169
+ /** Items in dependency order, given what each uses (`uses`, by index): what an item uses before it,
170
+ * items that use each other as one step, together; where the order leaves a choice, the step
171
+ * whose first item comes `before` the other's first goes first, and within a step `before` orders
172
+ * the items. The indices, in order. */
173
+ export function stepOrder(uses, before) {
174
+ const m = uses.length;
175
+ const comp = components(uses);
176
+ const nc = comp.reduce((x, c) => Math.max(x, c + 1), 0);
177
+ // each step's items in the order of choice; a step is chosen by its first
178
+ const steps = Array.from({ length: nc }, () => []);
179
+ for (let a = 0; a < m; a++)
180
+ steps[comp[a]].push(a);
181
+ for (const s of steps)
182
+ s.sort((a, b) => (before(a, b) ? -1 : before(b, a) ? 1 : 0));
183
+ // a step is ready once every step it uses is done
184
+ const waiting = new Int32Array(nc);
185
+ const usedBy = Array.from({ length: nc }, () => []);
186
+ const seen = new Set();
187
+ for (let a = 0; a < m; a++) {
188
+ for (const b of uses[a]) {
189
+ const c = comp[a], d = comp[b];
190
+ if (c === d || seen.has(c * nc + d))
191
+ continue;
192
+ seen.add(c * nc + d);
193
+ waiting[c]++;
194
+ usedBy[d].push(c);
195
+ }
196
+ }
197
+ const ready = new Heap((c, d) => before(steps[c][0], steps[d][0]));
198
+ for (let c = 0; c < nc; c++)
199
+ if (!waiting[c])
200
+ ready.push(c);
201
+ const out = [];
202
+ while (ready.size) {
203
+ const c = ready.pop();
204
+ for (const a of steps[c])
205
+ out.push(a);
206
+ for (const d of usedBy[c])
207
+ if (--waiting[d] === 0)
208
+ ready.push(d);
209
+ }
210
+ return out;
211
+ }
212
+ /** The strongly connected components of a graph given by its successor lists (Tarjan's, without
213
+ * recursion: a TCB can be deep): each node's component number. */
214
+ function components(succ) {
215
+ const n = succ.length;
216
+ const index = new Int32Array(n).fill(-1);
217
+ const low = new Int32Array(n);
218
+ const comp = new Int32Array(n).fill(-1);
219
+ const onStack = new Uint8Array(n);
220
+ const stack = [];
221
+ let next = 0, nc = 0;
222
+ const enter = (v) => {
223
+ index[v] = low[v] = next++;
224
+ stack.push(v);
225
+ onStack[v] = 1;
226
+ };
227
+ for (let s = 0; s < n; s++) {
228
+ if (index[s] >= 0)
229
+ continue;
230
+ enter(s);
231
+ // the path being explored: a node and how many of its successors it has looked at
232
+ const path = [[s, 0]];
233
+ while (path.length) {
234
+ const top = path[path.length - 1];
235
+ const v = top[0];
236
+ if (top[1] < succ[v].length) {
237
+ const w = succ[v][top[1]++];
238
+ if (index[w] < 0) {
239
+ enter(w);
240
+ path.push([w, 0]);
241
+ }
242
+ else if (onStack[w])
243
+ low[v] = Math.min(low[v], index[w]);
244
+ continue;
245
+ }
246
+ path.pop();
247
+ if (path.length) {
248
+ const u = path[path.length - 1][0];
249
+ low[u] = Math.min(low[u], low[v]);
250
+ }
251
+ if (low[v] === index[v]) {
252
+ let w;
253
+ do {
254
+ w = stack.pop();
255
+ onStack[w] = 0;
256
+ comp[w] = nc;
257
+ } while (w !== v);
258
+ nc++;
259
+ }
260
+ }
261
+ }
262
+ return comp;
263
+ }
264
+ /** A binary heap: `pop` gives the item that comes `before` all others. */
265
+ class Heap {
266
+ xs = [];
267
+ before;
268
+ constructor(before) {
269
+ this.before = before;
270
+ }
271
+ get size() {
272
+ return this.xs.length;
273
+ }
274
+ push(x) {
275
+ const xs = this.xs;
276
+ xs.push(x);
277
+ for (let k = xs.length - 1; k > 0;) {
278
+ const p = (k - 1) >> 1;
279
+ if (!this.before(xs[k], xs[p]))
280
+ break;
281
+ [xs[k], xs[p]] = [xs[p], xs[k]];
282
+ k = p;
283
+ }
284
+ }
285
+ pop() {
286
+ const xs = this.xs;
287
+ const top = xs[0];
288
+ const last = xs.pop();
289
+ if (xs.length) {
290
+ xs[0] = last;
291
+ for (let k = 0;;) {
292
+ const l = 2 * k + 1, r = l + 1;
293
+ let best = k;
294
+ if (l < xs.length && this.before(xs[l], xs[best]))
295
+ best = l;
296
+ if (r < xs.length && this.before(xs[r], xs[best]))
297
+ best = r;
298
+ if (best === k)
299
+ break;
300
+ [xs[k], xs[best]] = [xs[best], xs[k]];
301
+ k = best;
302
+ }
303
+ }
304
+ return top;
305
+ }
306
+ }
307
+ /** The shortest chain of statement and body references from `from` to `to`, both included: why
308
+ * `to` is in `from`'s TCB. Null when it is not. */
309
+ export function whyChain(g, from, to) {
310
+ const parent = new Int32Array(g.n).fill(-1);
311
+ parent[from] = from;
312
+ const queue = [from];
313
+ for (let k = 0; k < queue.length && parent[to] < 0; k++) {
314
+ const u = queue[k];
315
+ for (let e = g.outStart[u]; e < g.outStart[u + 1]; e++) {
316
+ const v = g.outDst[e];
317
+ if (!(g.outSite[e] & TCB_SITES) || parent[v] >= 0)
318
+ continue;
319
+ parent[v] = u;
320
+ queue.push(v);
321
+ }
322
+ }
323
+ if (parent[to] < 0)
324
+ return null;
325
+ const path = [to];
326
+ while (path[path.length - 1] !== from)
327
+ path.push(parent[path[path.length - 1]]);
328
+ return path.reverse();
329
+ }
330
+ /** Whether `reader` has given an opinion on the present version of a declaration. */
331
+ export function reviewedBy(d, reader, name, hash) {
332
+ return !!reader && current(d, name).some((o) => o.by === reader && fresh(o, hash));
333
+ }