@ceccec/millennium-solutions 0.1.1 → 9.0.6

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 (91) hide show
  1. package/.zenodo.json +33 -9
  2. package/ACCOUNTING.md +7 -7
  3. package/AXIOMS.md +559 -0
  4. package/CHALLENGES.md +2181 -2479
  5. package/CITATION.cff +1 -1
  6. package/DEPLOY.md +2 -2
  7. package/DEVELOP.md +1 -1
  8. package/LICENSE +6 -5
  9. package/PRIOR-ART.md +149 -0
  10. package/README.md +221 -123
  11. package/SIGNATURE.md +2 -2
  12. package/TRIAL.md +69 -0
  13. package/WHITEPAPER.md +5 -5
  14. package/captain.md +2 -2
  15. package/compare.md +4 -4
  16. package/compute.md +9 -0
  17. package/dashboard.md +1 -1
  18. package/examples.md +5 -5
  19. package/forensic.md +67 -0
  20. package/guide.md +5 -5
  21. package/index.md +209 -139
  22. package/package.json +88 -9
  23. package/paper.md +7183 -0
  24. package/quantum.md +42 -0
  25. package/rights.md +39 -0
  26. package/solutions.md +7 -7
  27. package/speedup.md +4 -4
  28. package/src/0/index.ts +12 -2
  29. package/src/1/index.ts +1 -1
  30. package/src/2/frequency-scales.ts +13 -1
  31. package/src/2/index.ts +1 -1
  32. package/src/3/index.ts +1 -1
  33. package/src/4/index.ts +1 -1
  34. package/src/5/index.ts +1 -1
  35. package/src/6/index.ts +1 -1
  36. package/src/7/index.ts +1 -1
  37. package/src/9/funding.ts +2 -2
  38. package/src/api/gates.ts +117 -0
  39. package/src/api/index.ts +488 -0
  40. package/src/api/lanes.ts +81 -0
  41. package/src/demand/queries.json +1764 -0
  42. package/src/face/index.ts +100 -0
  43. package/src/html/index.ts +10 -0
  44. package/src/latex/index.ts +474 -0
  45. package/src/millennium/index.ts +53 -0
  46. package/src/proof/README.md +1 -1
  47. package/src/proof/address.lean +117 -0
  48. package/src/proof/coin.lean +148 -0
  49. package/src/proof/covered.json +9 -0
  50. package/src/proof/demand.lean +112 -0
  51. package/src/proof/demand2.lean +171 -0
  52. package/src/proof/demand3.lean +117 -0
  53. package/src/proof/discovered.json +10965 -1864
  54. package/src/proof/elementary.lean +393 -0
  55. package/src/proof/energy.lean +215 -0
  56. package/src/proof/families.lean +435 -0
  57. package/src/proof/fixtures/axiom-control.lean +39 -0
  58. package/src/proof/fnv.lean +99 -0
  59. package/src/proof/generated-theorems.json +207 -0
  60. package/src/proof/generated.lean +101 -0
  61. package/src/proof/imagined.lean +492 -0
  62. package/src/proof/index.lean +98 -18
  63. package/src/proof/index.ts +2 -2
  64. package/src/proof/involution.lean +90 -0
  65. package/src/proof/ledgerclaims.lean +84 -0
  66. package/src/proof/light.lean +135 -0
  67. package/src/proof/mechanical.lean +421 -0
  68. package/src/proof/merkaba.lean +89 -0
  69. package/src/proof/merkle.lean +132 -0
  70. package/src/proof/nim.lean +105 -0
  71. package/src/proof/phenomena.lean +90 -0
  72. package/src/proof/priorart.lean +189 -0
  73. package/src/proof/quantum.lean +161 -0
  74. package/src/proof/reach.lean +84 -0
  75. package/src/proof/recovered.lean +56 -0
  76. package/src/proof/reversal.lean +73 -0
  77. package/src/proof/rights.lean +157 -0
  78. package/src/proof/sequences.lean +105 -0
  79. package/src/proof/speed.lean +129 -0
  80. package/src/proof/split.lean +180 -0
  81. package/src/proof/theorems.lean +48 -1
  82. package/src/proof/trial-all.json +15260 -0
  83. package/src/proof/z9.lean +92 -0
  84. package/src/proof/z9plus.lean +208 -0
  85. package/src/proofs.lisp +3 -3
  86. package/src/prove/emit.ts +115 -0
  87. package/src/prove/index.ts +143 -0
  88. package/src/prove/translate.ts +638 -0
  89. package/src/publication/index.ts +363 -0
  90. package/src/quantum/field.ts +73 -0
  91. package/src/quantum/tree.ts +79 -0
@@ -0,0 +1,488 @@
1
+ // THE REUSABLE API — one place that knows how to read this deposit.
2
+ //
3
+ // Thirty-two scripts loaded src/proof/discovered.json themselves and nine parsed Lean theorem names with
4
+ // their own regex. That is not a style problem: every one of those copies is a place where the deposit can
5
+ // start disagreeing with itself, and this session found several that had. forensics counted key families by
6
+ // splitting key TEXT and reported 377 development leads that did not exist; seal-lean matched whole keys and
7
+ // called 25 living theorems orphans; verify read a foreign ledger and reported every entry unproven. Each was
8
+ // a private answer to a question this module now answers once.
9
+ //
10
+ // Nothing here decides anything. It reads artefacts and returns them typed — the ledger, the Lean sources,
11
+ // and the relation between a sealed key and the theorem on disk that carries it. Judgement stays in the gates
12
+ // that own it.
13
+ import { readFileSync, readdirSync, existsSync, statSync } from 'node:fs'
14
+
15
+ export const LEDGER_PATH = 'src/proof/discovered.json'
16
+ export const PROOF_DIR = 'src/proof'
17
+
18
+ /** THE RECORD HAS FOUR STATES, NOT TWO. `revoked` was carrying two different meanings: a claim that stopped
19
+ * holding, and a claim that was withdrawn for want of a proof and has since been given one. Reporting the
20
+ * second as merely withdrawn understates the record — 113 entries whose statement the kernel now checks were
21
+ * being counted with the 1778 that nobody proved.
22
+ *
23
+ * Nothing is un-revoked. The original entry's own evidence is still a TypeScript test, and rewriting its
24
+ * status would erase the fact that it did not hold on what it had. What changes is that the record can now
25
+ * say both things at once: withdrawn on its own evidence, and STANDING through the theorem that carries it.
26
+ *
27
+ * standing — live, kernel-checked, nothing withdrawn
28
+ * carried — withdrawn on its own evidence, and its statement is proved by a live theorem
29
+ * withdrawn — withdrawn, and nothing proves it
30
+ * (revoked) — the raw flag, kept because the chain and every receipt were written against it
31
+ */
32
+ export type Status = 'standing' | 'carried' | 'withdrawn'
33
+
34
+ export interface Entry {
35
+ key: string
36
+ name: string
37
+ receipt: string
38
+ revoked?: boolean
39
+ reason?: string
40
+ supersededBy?: string
41
+ portable?: boolean
42
+ statement?: string
43
+ }
44
+
45
+ /** The whole record, in order. Append-only: withdrawn entries are present and marked, never removed.
46
+ *
47
+ * CACHED ON THE FILE'S IDENTITY, because it was read 4252 times in a single `pages.ts` run. Every helper
48
+ * below defaults its parameter to `ledger()`, so `live()`, `liveKeys()`, `byKey()` and `statusOf()` each
49
+ * re-read and re-parsed the whole 2418-entry file when called without one — and called inside a loop, that
50
+ * is megabytes of JSON parsed thousands of times. A CPU profile put 7.1s of pages.ts's 16s in this function
51
+ * and 7.5s in readFileSync beneath it; the site build is 80% of a deploy and this was 45% of its pre-build.
52
+ *
53
+ * The key is mtime+size, NOT a plain memo. seal-lean.ts and carry.ts WRITE this file and then read it back,
54
+ * and a cache that ignored that would serve them the record as it was before their own append — which in an
55
+ * append-only ledger is the one stale read that could cause real damage. A statSync per call is the price,
56
+ * and it is a rounding error against a parse. */
57
+ let _lcache: { key: string; rows: Entry[] } | null = null
58
+ export const ledger = (): Entry[] => {
59
+ if (!existsSync(LEDGER_PATH)) return []
60
+ const st = statSync(LEDGER_PATH)
61
+ const key = `${st.mtimeMs}:${st.size}`
62
+ if (_lcache && _lcache.key === key) return _lcache.rows
63
+ const rows = JSON.parse(readFileSync(LEDGER_PATH, 'utf8')) as Entry[]
64
+ _lcache = { key, rows }
65
+ return rows
66
+ }
67
+
68
+ /** The entries that STAND. Everything that judges what the deposit currently claims wants this one. */
69
+ export const live = (l: Entry[] = ledger()): Entry[] => l.filter((e) => !e.revoked)
70
+
71
+ /** Withdrawn but kept — still receipted, still in the chain, no longer claimed. */
72
+ export const withdrawn = (l: Entry[] = ledger()): Entry[] => l.filter((e) => e.revoked === true)
73
+
74
+ /** Withdrawn AND since re-established by a Lean theorem, which the record links rather than un-revoking. */
75
+ export const superseded = (l: Entry[] = ledger()): Entry[] => l.filter((e) => e.supersededBy)
76
+
77
+ export const liveKeys = (l: Entry[] = ledger()): Set<string> => new Set(live(l).map((e) => e.key))
78
+ export const byKey = (l: Entry[] = ledger()): Map<string, Entry> => new Map(l.map((e) => [e.key, e]))
79
+
80
+ /** The octave reading — the deposit counts in eights. A target the theorems earn, never a quota. */
81
+ export const octave = (l: Entry[] = ledger()) => ({
82
+ total: l.length, octaves: Math.floor(l.length / 8), remainder: l.length % 8, exact: l.length % 8 === 0,
83
+ })
84
+
85
+ export interface LeanTheorem { name: string; file: string; tactic: string; statement: string; namespace: string }
86
+
87
+ export const leanFiles = (): string[] =>
88
+ existsSync(PROOF_DIR) ? readdirSync(PROOF_DIR).filter((f) => f.endsWith('.lean')).sort() : []
89
+
90
+ export const leanSource = (file: string): string => readFileSync(`${PROOF_DIR}/${file}`, 'utf8')
91
+
92
+ /** Every theorem on disk, with the file that carries it and the tactic that closed it. ONE parse of the
93
+ * `theorem` syntax, so a change to how proofs are written is a change in one place. */
94
+ /** A theorem's statement text, with comments removed and whitespace flattened.
95
+ *
96
+ * A TRAILING COMMENT IS NOT PART OF THE FORMULA. Stripping only whole-line comments (`^\s*--`) left a
97
+ * comment that trailed a CONTINUED line embedded in the statement, and the page then printed
98
+ * `... == d) -- σ ∘ σ = id everywhere — the shared involution ∧ ((List.range 10)...` — English spliced
99
+ * between two conjuncts, published as mathematics. A comment runs to end of line wherever it starts. */
100
+ export const normalizeStatement = (raw: string): string =>
101
+ raw.split('\n').map((l) => l.replace(/(^|\s)--.*$/, '')).join(' ').replace(/\s+/g, ' ').trim()
102
+
103
+ export const leanTheorems = (): LeanTheorem[] => {
104
+ const out: LeanTheorem[] = []
105
+ for (const file of leanFiles()) {
106
+ const src = leanSource(file)
107
+ const ns = src.match(/^namespace\s+([A-Za-z_0-9.]+)/m)?.[1] ?? file.replace('.lean', '')
108
+ for (const m of src.matchAll(/^theorem\s+([A-Za-z_0-9]+)\s*:([\s\S]*?):=\s*(by decide|rfl|by\s+\w+)/gm))
109
+ out.push({ name: m[1], file, namespace: ns, tactic: m[3], statement: normalizeStatement(m[2]) })
110
+ }
111
+ return out
112
+ }
113
+
114
+ /** Which file carries the theorem a sealed key was minted from.
115
+ *
116
+ * Keys carry their naming HISTORY: older entries are `lean_<theorem>` and current ones are
117
+ * `lean_<namespace>_<theorem>`. Comparing whole keys called 25 living theorems orphans, and splitting key
118
+ * text invented a family per theorem. Matching on the theorem IDENTIFIER is what survives both conventions,
119
+ * and it is the only comparison any caller should be making. */
120
+ /** The domain `by decide` actually walked — the count of cases the kernel exhausted, read off the
121
+ * statement. One definition: scripts/leandoc.ts and scripts/pages.ts each had their own copy of this,
122
+ * byte-identical, so the number a theorem page prints and the number the front pages rank by could
123
+ * drift apart on the next edit to either. */
124
+ export const domainOf = (statement: string): number => {
125
+ let n = 1
126
+ for (const m of statement.matchAll(/List\.range'\s+\d+\s+(\d+)/g)) n *= Number(m[1])
127
+ for (const m of statement.matchAll(/List\.range\s+(\d+)/g)) n *= Number(m[1])
128
+ for (const m of statement.matchAll(/\[([0-9,\s]+)\]/g)) n *= Math.max(1, m[1].split(',').filter((x) => x.trim()).length)
129
+ return n
130
+ }
131
+
132
+ /** How many theorems there are, and how many ledger keys name them — which are NOT the same number.
133
+ *
134
+ * The deposit published its live-key count as though it were a count of theorems. It is not: src/proof holds
135
+ * the theorems; the ledger once held more live keys than that, because 24 carried two — one minted
136
+ * before keys had a namespace (`lean_units_are_six`) and one after (`lean_z9_units_are_six`). Both were live
137
+ * and both are legitimate history, but counting a key as a theorem overstates the deposit. Those 24 second
138
+ * addresses were retired in favour of their namespaced form, so no theorem now carries two. The 8 `rfl`
139
+ * DECLARATIONS have no key at all, because seal-lean.ts seals `by decide` only — and they are declarations,
140
+ * never theorems, by the definition this file states.
141
+ *
142
+ * Lean decides what a theorem is. Every count of theorems is taken from src/proof; a count of keys is
143
+ * called a count of keys. */
144
+ export interface Census {
145
+ theorems: number; byDecide: number; rfl: number
146
+ liveKeys: number; sealedTheorems: number; surplusKeys: number; unresolvableKeys: number; unsealed: number
147
+ }
148
+
149
+ /** THE CLAY FLOOR, MEASURED OVER THE TREE — not certified by a constant.
150
+ *
151
+ * index.lean used to carry `def provenHere : Nat := 0` with `the_floor_is_zero_of_seven := rfl` beside it,
152
+ * and that literal glued as a conjunct onto every theorem. The repo's own trial already said what was wrong
153
+ * with it (finding seventeen): "closed by reflexivity on a constant this file declares … written as seven it
154
+ * would be equally green, and therefore it supports neither number." A counter the author maintains is an
155
+ * arbiter the author writes.
156
+ *
157
+ * An absence is not established by a certificate. It is established by there being no proof that reaches —
158
+ * a property of the tree, checkable over the tree, and REFUTABLE: add one theorem quantifying over ℝ, or
159
+ * naming the ζ-zeros, and this stops holding. That is what makes it worth running.
160
+ *
161
+ * It measures three things about the Clay-named theorems: that all seven are present and closed by `decide`;
162
+ * the largest finite domain any of them walks; and whether any statement reaches for an object the
163
+ * conjectures actually concern. */
164
+ export interface ClayFloor {
165
+ seven: number; inFile: number; allByDecide: boolean; largestDomain: number; reaches: string[]; holds: boolean
166
+ }
167
+
168
+ /** The objects the seven conjectures are about. A statement mentioning one of these is reaching past the
169
+ * finite algebra this deposit decides, which is exactly what must not happen silently. */
170
+ export const CONJECTURE_OBJECTS = [
171
+ 'ℝ', 'ℂ', 'Real', 'Complex', 'zeta', 'critical line', 'polynomial time', 'viscosity', 'gauge',
172
+ 'mass gap', 'quantum field', 'cohomolog', 'algebraic cycle', 'elliptic curve', 'l-function',
173
+ 'manifold', 'homeomorph', '3-sphere',
174
+ ]
175
+
176
+ const CLAY_NAMED = /riemann|p_vs_np|navier|yang|hodge|birch|poincare/
177
+
178
+ /** THE VERIFICATION ADVANTAGE, read off src/proof/speed.lean rather than retyped.
179
+ *
180
+ * The deposit proves this and does not say it where anyone reads: measured over the front pages, four of
181
+ * eight proved properties were stated, and the four missing ones were the whole advantage. A boundary that
182
+ * is stated loudly while the result is left in a source file is not modesty, it is a page that misinforms
183
+ * in the other direction.
184
+ *
185
+ * Everything here comes from the `def`s the theorems in that file decide over, so the page and the kernel
186
+ * cannot disagree, and none of it is a number typed into prose. */
187
+ export interface Advantage {
188
+ recomputeUs: number; verifyUs: number; nsPerVerify: number; leaves: number; rounds: number; ratio: number
189
+ }
190
+
191
+ export const advantage = (): Advantage => {
192
+ const src = leanSource('speed.lean')
193
+ const num = (name: string): number => {
194
+ const m = src.match(new RegExp('def\\s+' + name + '\\s*:\\s*Nat\\s*:=\\s*(\\d+)'))
195
+ if (!m) throw new Error('speed.lean no longer defines ' + name + ' — the advantage cannot be reported without it')
196
+ return Number(m[1])
197
+ }
198
+ const recomputeUs = num('recomputeUs'), verifyUs = num('verifyUs'), nsPerVerify = num('nsPerVerify')
199
+ const leaves = 2 ** 20
200
+ return { recomputeUs, verifyUs, nsPerVerify, leaves, rounds: 20, ratio: Math.floor(recomputeUs / verifyUs) }
201
+ }
202
+
203
+ export const clayFloor = (): ClayFloor => {
204
+ // EVERY THEOREM IN THE CLAY-NAMED FILE, not only the seven that carry a problem's name. index.lean holds
205
+ // eight: the seven, and `the_seven_rest_on_one_finite_structure`, the involution they share. Scanning only
206
+ // the name-matched seven left that eighth unmeasured — it could have reached for a conjecture object and
207
+ // this check would not have seen it, which is the hole the narrow filter opened.
208
+ const inFile = leanTheorems().filter((t) => t.file === 'index.lean')
209
+ const seven = inFile.filter((t) => CLAY_NAMED.test(t.name))
210
+ const text = inFile.map((t) => t.statement).join(' ')
211
+ const reaches = CONJECTURE_OBJECTS.filter((o) => new RegExp(o.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'), 'i').test(text))
212
+ let largest = 0
213
+ for (const t of inFile) largest = Math.max(largest, domainOf(t.statement))
214
+ const allByDecide = inFile.length > 0 && inFile.every((t) => t.tactic === 'by decide')
215
+ return {
216
+ seven: seven.length,
217
+ inFile: inFile.length,
218
+ allByDecide,
219
+ largestDomain: largest,
220
+ reaches,
221
+ holds: seven.length === 7 && allByDecide && reaches.length === 0,
222
+ }
223
+ }
224
+
225
+ /** WHAT A THEOREM OF THIS DEPOSIT IS — one definition, because the word was carrying four.
226
+ *
227
+ * Asked for the exact definition, the deposit could produce four different numbers, and all four were in
228
+ * use in its own pages:
229
+ *
230
+ * a `theorem` declaration the kernel accepts — sorry-free, axiom-free, in src/proof
231
+ * of those, closed by EXHAUSTION (`by decide`) — the kernel evaluated the proposition at every point
232
+ * a live key in the ledger — an ADDRESS; 0 theorems carry two, one resolves to none
233
+ * an entry in the append-only ledger — a RECEIPT, and most of them are withdrawn
234
+ *
235
+ * The repo's own rule already chose, in seal-lean.ts: "`by decide` is algebra — the kernel evaluates the
236
+ * proposition over its whole finite domain and the result is a computation, not a convention. `rfl` on a
237
+ * declared constant proves the declaration and nothing else — it is not evidence." That is why seal-lean
238
+ * seals `by decide` and refuses `rfl`, and it is the line taken here.
239
+ *
240
+ * A THEOREM OF THIS DEPOSIT is a declaration in src/proof that the Lean kernel accepts, free of `sorry`
241
+ * and of axioms, and CLOSED BY EXHAUSTION over a stated finite domain. A declaration closed by `rfl` is a
242
+ * definitional unfolding and is counted separately, never as a theorem. A key is an address and an entry
243
+ * is a receipt; neither is a theorem, and scripts/contradictions.ts fails the build when either count is
244
+ * printed next to the word. */
245
+ export const THEOREM_DEFINITION =
246
+ 'a declaration in src/proof that the Lean kernel accepts — sorry-free, axiom-free — and closes by exhaustion over a stated finite domain'
247
+
248
+ /** The count that definition yields. Everything reporting "theorems" should read this. */
249
+ export const theoremCount = (): number => census().byDecide
250
+
251
+ /** THE DIGIT SPLIT, read off src/proof/split.lean rather than retyped.
252
+ *
253
+ * Measured after the theorems were written: not one of them reached the front pages. They were in the
254
+ * collected paper — a hundred pages — and on their own theorem pages, and a reader of the homepage saw
255
+ * none of it, including the answer to what paying the two coins buys. Proved and unsaid is the underclaim
256
+ * this deposit had no guard against until the inverse ratchet.
257
+ *
258
+ * Every value comes from the `def`s the kernel decides over in that file, so the page cannot drift from
259
+ * the theorems and nothing here is a number typed into prose. */
260
+ export interface Split {
261
+ tokens: number[]; singles: number[]; coins: number; coinStep: number; sealBits: number
262
+ exhaustible: number[]; halting: number[]; payments: number; orbitPeriod: number
263
+ }
264
+
265
+ export const split = (): Split => {
266
+ const src = leanSource('split.lean')
267
+ const list = (name: string): number[] => {
268
+ const m = src.match(new RegExp('def\\s+' + name + '\\s*:\\s*List Nat\\s*:=\\s*\\[([^\\]]*)\\]'))
269
+ if (!m) throw new Error('split.lean no longer defines ' + name)
270
+ return m[1].split(',').map((x) => Number(x.trim()))
271
+ }
272
+ const num = (name: string): number => {
273
+ // A plain numeral only. `eval` on a source file is how a build starts executing whatever the file
274
+ // happens to contain, and reading a constant does not need it.
275
+ const m = src.match(new RegExp('def\\s+' + name + '\\s*:\\s*Nat\\s*:=\\s*(\\d+)'))
276
+ if (!m) throw new Error('split.lean no longer defines ' + name + ' as a plain numeral')
277
+ return Number(m[1])
278
+ }
279
+ const tokens = list('tokens'), singles = list('singles')
280
+ const coins = num('coins'), coinStep = 3 * coins, sealBits = num('sealBits')
281
+ return {
282
+ tokens, singles, coins, coinStep, sealBits,
283
+ exhaustible: tokens.filter((t) => t % coinStep === 0),
284
+ halting: tokens.filter((t) => t % coinStep !== 0),
285
+ payments: sealBits / coins,
286
+ orbitPeriod: 6,
287
+ }
288
+ }
289
+
290
+ export const census = (): Census => {
291
+ const T = leanTheorems()
292
+ const keys = (live() as { key: string }[]).filter((e) => e.key.startsWith('lean_')).map((e) => e.key)
293
+ const named = new Set<string>()
294
+ let matched = 0
295
+ for (const k of keys) {
296
+ const t = theoremOfKey(k, T)
297
+ if (t) { named.add(t.file + '::' + t.name); matched++ }
298
+ }
299
+ // The arithmetic closes exactly, and is asserted in scripts/contradictions.ts:
300
+ // liveKeys = sealedTheorems + surplusKeys + unresolvableKeys
301
+ // The unresolvable one is `lean_add_group`, whose name is declared in two files and whose key predates
302
+ // namespaces, so no single theorem can be said to be the one it was minted from.
303
+ return {
304
+ theorems: T.length,
305
+ byDecide: T.filter((t) => t.tactic === 'by decide').length,
306
+ rfl: T.filter((t) => t.tactic === 'rfl').length,
307
+ liveKeys: keys.length,
308
+ sealedTheorems: named.size,
309
+ surplusKeys: matched - named.size,
310
+ unresolvableKeys: keys.length - matched,
311
+ unsealed: T.length - named.size,
312
+ }
313
+ }
314
+
315
+ export const theoremOfKey = (key: string, thms: LeanTheorem[] = leanTheorems()): LeanTheorem | null => {
316
+ const rest = key.replace(/^lean_/, '')
317
+ const flat = (x: string) => x.toLowerCase().replace(/[^a-z0-9]/g, '')
318
+
319
+ // A NAME IS NOT AN IDENTIFIER WHEN IT IS NOT UNIQUE. `add_group` is declared in both mechanical.lean and
320
+ // z9.lean with DIFFERENT statements, so matching on the trailing name alone sent lean_z9_add_group to
321
+ // mechanical.lean — the page then printed a formula that was not the one its key was minted from. The
322
+ // namespace the key carries is what separates them, so it is tried first, exactly.
323
+ const named = thms.filter((t) => rest === t.name || rest.endsWith('_' + t.name) || rest.endsWith('.' + t.name))
324
+ if (named.length <= 1) return named[0] ?? null
325
+ for (const t of named) {
326
+ const prefix = rest.slice(0, rest.length - t.name.length).replace(/[_.]$/, '')
327
+ if (prefix && (flat(prefix) === flat(t.namespace) || flat(prefix) === flat(t.file.replace('.lean', '')))) return t
328
+ }
329
+ // Ambiguous: the name is shared and the key does not say which. Returning one at random is how the wrong
330
+ // statement got onto a page, so this returns nothing and the caller shows nothing.
331
+ return null
332
+ }
333
+
334
+ /** The file alone, for callers that only need to know where a key was minted from. Delegates to
335
+ * theoremOfKey so the two-convention matching above is written once and cannot drift between them. */
336
+ export const fileOfKey = (key: string, thms: LeanTheorem[] = leanTheorems()): string | null =>
337
+ theoremOfKey(key, thms)?.file ?? null
338
+
339
+ /** Frontmatter written as `-- key: value` at the head of a Lean file, before its prose. */
340
+ export const frontmatter = (file: string): Record<string, string> => {
341
+ const fm: Record<string, string> = {}
342
+ for (const line of leanSource(file).split('\n')) {
343
+ const m = line.match(/^\s*--\s*([a-z][a-z0-9_]*):\s*(.+?)\s*$/)
344
+ if (!m) { if (/^\s*--/.test(line)) continue; if (line.trim() === '' || /^(import|set_option)/.test(line)) continue; break }
345
+ fm[m[1]] = m[2]
346
+ }
347
+ return fm
348
+ }
349
+
350
+ /** THE TWO QUESTIONS, ASKED ONE WAY. Eight scripts spelled "does this entry stand" as their own inline
351
+ * predicate — `!e.revoked` here, `filter((e) => e.revoked)` there, `e.revoked === true` elsewhere. The
352
+ * predicates happened to agree, but agreement by coincidence is what a shared definition removes: a
353
+ * withdrawn entry that later grows a third state (superseded is already one) would need finding in eight
354
+ * places, and this session has twice watched a private copy answer differently from its siblings. */
355
+ export const isLive = (e: Entry): boolean => !e.revoked
356
+
357
+ /** The state of one entry, as the record can actually justify it.
358
+ *
359
+ * THE SUCCESSOR MUST ITSELF STAND. The first version of this read `e.supersededBy ? 'carried' : 'withdrawn'`
360
+ * — the mere PRESENCE of a forwarding key was taken as proof that the statement survives. Twenty-five entries
361
+ * forward to a key that is itself withdrawn, and every one of them was being reported as carried: the record
362
+ * claimed a live proof at the far end of a link that leads nowhere. CHALLENGES.md published those links, and
363
+ * the seal gate caught a page citing a withdrawn theorem, which is how this was found rather than shipped.
364
+ * A carry is a claim about where the proof is now, so it is only a carry when there is a proof there.
365
+ *
366
+ * AND THE LINK CAN BE MORE THAN ONE HOP. That fix stopped at the first heir, and three claims were reading
367
+ * as withdrawn while their proof was two links away: `add_group` forwards to `lean_add_group`, which was
368
+ * itself retired when the file-prefixed address format arrived and forwards to `lean_z9_add_group`, which
369
+ * is live and decides exactly the claim. The record said where the proof went, twice, and the reader
370
+ * stopped after the first sentence. Walking the chain does NOT weaken the check above — a chain ending at
371
+ * a revoked entry with nowhere further to go is still withdrawn, which is the whole content of "the
372
+ * successor must itself stand". A cycle or an implausibly long chain is withdrawn too: the record has
373
+ * stopped making sense at that point, and reporting a carry on it would be trusting a loop. */
374
+ export const CARRY_HOPS = 16
375
+
376
+ /** The key that actually holds the proof for a carried entry — the LIVE end of the forwarding chain, not
377
+ * the first link. CHALLENGES.md published the first link and the seal gate refused the page: `euler_units_pow6`
378
+ * forwards to `lean_euler_units_pow_six`, which is retired and forwards on to `lean_z9_euler_units_pow_six`.
379
+ * Citing the middle of a chain sends a reader to a page that says the theorem moved. One walk, used by both
380
+ * `statusOf` and every surface that prints where the proof went, so the status and the citation cannot
381
+ * disagree about which key that is. */
382
+ export const carrierOf = (e: Entry, l: Entry[] = ledger()): string | null => {
383
+ if (!e.revoked) return null
384
+ const seen = new Set<string>([e.key])
385
+ let cur = e
386
+ for (let hop = 0; hop < CARRY_HOPS; hop++) {
387
+ if (!cur.supersededBy || seen.has(cur.supersededBy)) return null
388
+ seen.add(cur.supersededBy)
389
+ const heir = l.find((x) => x.key === cur.supersededBy)
390
+ if (!heir) return null
391
+ if (!heir.revoked) return heir.key
392
+ cur = heir
393
+ }
394
+ return null
395
+ }
396
+
397
+ export const statusOf = (e: Entry, l: Entry[] = ledger()): Status => {
398
+ if (!e.revoked) return 'standing'
399
+ return carrierOf(e, l) ? 'carried' : 'withdrawn'
400
+ }
401
+
402
+ /** Entries whose statement stands, whether by their own seal or through the theorem that carries it. This is
403
+ * the honest answer to "how much of this deposit is proved" — it is NOT the same as `live`, which counts
404
+ * only the entries that carry their own proof, and both numbers are worth reporting separately. */
405
+ export const carried = (l: Entry[] = ledger()): Entry[] => l.filter((e) => statusOf(e, l) === 'carried')
406
+ export const proved = (l: Entry[] = ledger()): Entry[] => l.filter((e) => statusOf(e, l) !== 'withdrawn')
407
+ export const isWithdrawn = (e: Entry): boolean => e.revoked === true
408
+
409
+ // ── THE ℤ/9 SETS, COMPUTED AND CHECKED AGAINST THE THEOREM THAT PROVES THEM ────────────────────────────────
410
+ //
411
+ // The tooling had these written out as literals — `[1,2,4,5,7,8]` for the units, `[3,6,0]` for the triad,
412
+ // `[1,2,4,8,7,5]` for the orbit — in imagine.ts, fold.ts and pages.ts. Every one of them is COMPUTED in
413
+ // src/0 and PROVED in src/proof, so the literal was a third copy that no gate was checking: change the
414
+ // modulus and the runtime and the kernel would both follow while the tooling silently kept describing ℤ/9.
415
+ //
416
+ // These compute the set and then refuse to return it unless the sealed theorem that proves it is live in the
417
+ // ledger. That is the whole point — not that the numbers are right today, but that the tooling cannot go on
418
+ // using them after the proof behind them stops standing.
419
+ import { units as runtimeUnits, triad as runtimeTriad, vortexOrbit as runtimeOrbit } from '../0/index.ts'
420
+
421
+ /** Is a live key addressing this THEOREM, whatever key it is filed under?
422
+ *
423
+ * The check used to require one exact key string, and retiring 24 duplicate addresses broke it instantly:
424
+ * `lean_units_are_six` was withdrawn in favour of `lean_z9_units_are_six`, the theorem never stopped being
425
+ * proved, and the API refused to serve the units. The guarantee is meant to be "a live theorem proves
426
+ * this", and a key is an ADDRESS — the same distinction that put 330 dead URLs in the deposition records
427
+ * this morning, here in the one place that decides whether the tooling may use a value at all.
428
+ *
429
+ * Resolving through theoremOfKey means the guarantee survives a rename or a re-address and still fails
430
+ * when the proof itself goes — which is what it was for. */
431
+ // MEMOISED, because the first version was O(live keys × theorems) PER CALL and units() is called from
432
+ // everywhere: it resolved every live key against every kernel-accepted declaration on each invocation
433
+ // and took `npm run ci:local` past ten minutes. The set of names reachable from a live key is built once,
434
+ // on the same mtime+size key the ledger cache uses, so a script that writes the ledger and reads it back
435
+ // still sees its own append.
436
+ let _provenCache: { key: string; names: Set<string> } | null = null
437
+ const provenNames = (): Set<string> => {
438
+ const st = existsSync(LEDGER_PATH) ? statSync(LEDGER_PATH) : null
439
+ const ck = st ? `${st.mtimeMs}:${st.size}` : 'none'
440
+ if (_provenCache && _provenCache.key === ck) return _provenCache.names
441
+ const thms = leanTheorems()
442
+ const names = new Set<string>()
443
+ for (const k of liveKeys()) { const t = theoremOfKey(k, thms); if (t) names.add(t.name) }
444
+ _provenCache = { key: ck, names }
445
+ return names
446
+ }
447
+
448
+ const provenLive = (key: string): boolean => {
449
+ if (liveKeys().has(key)) return true
450
+ const want = theoremOfKey(key, leanTheorems())
451
+ return want ? provenNames().has(want.name) : false
452
+ }
453
+
454
+ const backedBy = (key: string, value: number[], what: string): number[] => {
455
+ if (!provenLive(key)) {
456
+ throw new Error(
457
+ `api: refusing to serve ${what} — the theorem that proves it (${key}) is not live in the ledger. ` +
458
+ `The value would still compute; what is missing is the reason to trust it. Prove it, or stop using it.`)
459
+ }
460
+ return value
461
+ }
462
+
463
+ /** The six units of ℤ/9 — computed, and served only while lean_units_are_six stands. */
464
+ export const units = (): number[] => backedBy('lean_units_are_six', runtimeUnits(), 'the units')
465
+
466
+ /** The triad {3,6,9} — the non-units, the merkaba's axis. */
467
+ export const triad = (): number[] => backedBy('lean_units_are_six', runtimeTriad(), 'the triad')
468
+
469
+ /** The doubling orbit 1,2,4,8,7,5 — six turns before it returns to where it began. */
470
+ export const orbit = (): number[] =>
471
+ backedBy('lean_millenniumfloor_riemann_reflection_and_heart', runtimeOrbit(), 'the doubling orbit')
472
+
473
+ /** THE MOD-3 CLASSES — the merkaba's axis and its two tetrahedra, computed from the partition rather than
474
+ * typed. These were the literals I missed when I claimed the tooling held no hardcoded ℤ/9 sets: the claim
475
+ * was scoped to the three patterns I had grepped for, which is how a check confirms what it was told to look
476
+ * for and nothing else. Backed by the theorem that proves the classes partition the ring 3+3+3. */
477
+ export const tetA = (): number[] =>
478
+ backedBy('lean_merkaba_the_three_classes_partition_z9',
479
+ Array.from({ length: 9 }, (_, d) => d).filter((d) => d % 3 === 1), 'the first tetrahedron')
480
+
481
+ export const tetB = (): number[] =>
482
+ backedBy('lean_merkaba_the_three_classes_partition_z9',
483
+ Array.from({ length: 9 }, (_, d) => d).filter((d) => d % 3 === 2), 'the second tetrahedron')
484
+
485
+ /** The axis {3,6,0} — the same partition's third class, and the triad by another name. */
486
+ export const axis = (): number[] =>
487
+ backedBy('lean_merkaba_the_three_classes_partition_z9',
488
+ Array.from({ length: 9 }, (_, d) => d).filter((d) => d % 3 === 0), 'the axis')
@@ -0,0 +1,81 @@
1
+ /** ── HOW MANY JOBS THIS MACHINE CAN ACTUALLY HOLD, WHICH IS NOT HOW MANY CORES IT HAS ─────────────────────
2
+ *
3
+ * `availableParallelism()` answers "how many things can run at once" and says nothing about whether they
4
+ * FIT. On this host that gap is not academic — measured, one `lean` process elaborating families.lean
5
+ * peaks at about 2.9 GB, so ten lanes is 29 GB of demand on a 32 GB machine from a single session. The
6
+ * machine does not refuse; it swaps, and the run gets slower while every core reads as busy.
7
+ *
8
+ * A peer session on uuidna reported a window where this host sat at 88% system time against 9% user with
9
+ * swap climbing — ten times more kernel than work, a machine moving pages rather than computing — and
10
+ * named the cause as a capacity function that is honest per process and blind to its neighbours. I could
11
+ * not verify that recording: the host has rebooted since and currently shows no swap in use at all. What I
12
+ * could verify is the arithmetic, and the arithmetic is worse than the framing, because cores were never
13
+ * the binding constraint. Memory is.
14
+ *
15
+ * So a lane budget is the smallest of three numbers, and the third is the neighbour term:
16
+ *
17
+ * cores what can run at once
18
+ * memory / perJob what fits, from memory that is actually reclaimable
19
+ * minus neighbours jobs of this kind already running, whoever started them
20
+ *
21
+ * THE NEIGHBOUR TERM NEEDS NO AGREEMENT BETWEEN SESSIONS. A shared budget would have to be negotiated,
22
+ * which means a protocol, which means every repo adopting it before any repo benefits. Yielding to jobs
23
+ * that are already running is unilateral: if each session does it alone, the sum is bounded anyway.
24
+ */
25
+ import { availableParallelism, totalmem } from 'node:os'
26
+ import { execFileSync } from 'node:child_process'
27
+
28
+ /** Memory a checker may actually take: free plus the pages the OS can reclaim without swapping. `freemem()`
29
+ * counts only the free list and reports a few gigabytes on a machine with twenty reclaimable — budgeting
30
+ * from it would refuse lanes the machine can easily afford. */
31
+ export const reclaimableMB = (): number => {
32
+ try {
33
+ const out = String(execFileSync('vm_stat', [], { stdio: 'pipe' }))
34
+ const page = Number(out.match(/page size of (\d+)/)?.[1] ?? 4096)
35
+ const pages = (k: string) => Number(out.match(new RegExp(`Pages ${k}:\\s+(\\d+)`))?.[1] ?? 0)
36
+ const usable = pages('free') + pages('inactive') + pages('speculative') + pages('purgeable')
37
+ if (usable > 0) return (usable * page) / (1024 * 1024)
38
+ } catch { /* not macOS, or vm_stat unavailable — fall through */ }
39
+ return (totalmem() / (1024 * 1024)) / 2 // half of physical: a guess, and named as one
40
+ }
41
+
42
+ /** Jobs of this kind already running, whoever started them — or NULL when it could not be measured.
43
+ *
44
+ * The first version ran `pgrep -c`, counted the output, and caught every failure as zero. macOS pgrep has
45
+ * no `-c` flag — that is Linux — so it printed a usage error to stderr, threw, and the catch reported "no
46
+ * neighbours" on every call. The term never fired once, and nothing said so: a check that cannot run,
47
+ * answering all-clear. Three answers, not two, is the rule that catches this — none, some, and unknown are
48
+ * different, and only the first two are a measurement. */
49
+ export const neighbours = (procName: string): number | null => {
50
+ try {
51
+ const out = String(execFileSync('pgrep', ['-x', procName], { stdio: ['ignore', 'pipe', 'ignore'] }))
52
+ return out.split('\n').filter((l) => l.trim().length).length
53
+ } catch (e: any) {
54
+ // pgrep exits 1 with NO output when nothing matched: that is a real measurement of zero.
55
+ if (e?.status === 1 && !String(e?.stdout ?? '').trim()) return 0
56
+ return null // anything else — missing pgrep, wrong flag, no permission — is UNKNOWN, not zero
57
+ }
58
+ }
59
+
60
+ export type Budget = { lanes: number; cores: number; byMemory: number; running: number | null; perJobMB: number; why: string }
61
+
62
+ /** The lane count, with every term it was derived from, so a caller can print WHY rather than a bare number. */
63
+ export const laneBudget = (opts: { perJobMB: number; procName: string; envLanes?: string }): Budget => {
64
+ const cores = availableParallelism?.() ?? 4
65
+ const forced = Number(opts.envLanes ?? '')
66
+ const perJobMB = Math.max(1, opts.perJobMB)
67
+ const byMemory = Math.max(1, Math.floor(reclaimableMB() / perJobMB))
68
+ const running = neighbours(opts.procName)
69
+ // UNKNOWN IS NOT ZERO. When the neighbour count cannot be measured the budget halves rather than assuming
70
+ // the machine is empty — the failure that matters here is claiming capacity somebody else is already using.
71
+ const base = Math.min(cores, byMemory)
72
+ const lanes = forced > 0 ? forced
73
+ : running === null ? Math.max(1, Math.floor(base / 2))
74
+ : Math.max(1, base - running)
75
+ const why = forced > 0
76
+ ? `forced to ${forced} by the environment`
77
+ : running === null
78
+ ? `min(cores ${cores}, memory ${byMemory} at ~${perJobMB}MB each) halved — the neighbour count could NOT be measured on this host`
79
+ : `min(cores ${cores}, memory ${byMemory} at ~${perJobMB}MB each) minus ${running} already running`
80
+ return { lanes, cores, byMemory, running, perJobMB, why }
81
+ }