Today every category on the OuterTalk map sits on a rail that a formula fixes. A proposal asked whether positions should come from the data instead — from what posts cite and what they say. Six independent checks and two adversarial passes went at it. Three of its ten claims were wrong, one of them about the wall between money and meaning. This page keeps the corrections and the numbers.
Five terms carry this page. They are defined here once.
The OuterTalk map is a graph: 42 top-level categories, each on its own rail — one spoke of a spiral — with concepts and posts hung beneath. (It is a DAG, a directed acyclic graph: every edge points one way and nothing loops back.) Today the position of every rail is welded: a pure function of lineage — which category, which slot — written in one file that imports nothing. No data moves it.
The proposal was to compute positions instead. Build a graph from what posts cite and what they say, take its Laplacian, and let the layout fall out of the eigenvectors. (The Laplacian is L = D − A: the degree matrix minus the adjacency matrix. Its eigenvectors are the graph's natural modes; its eigenvalues say how fast each mode varies.) Then a category would be something the corpus produces, not something we draw.
One rule sits under all of it: the wall. Money may set how bright a thing is, and never where it is or whether it is true. The rules for that are on the concept-token page. Every claim below was checked against it.
src/utils/spiralMath.ts declares the invariant at line 7 and imports nothing. railPoint(m, i) is a pure function of rail and slot. Unbuyable by construction: there is no input a buyer could reach.
Whatever produces the edges produces the map. What can be computed can be manufactured. Whether that trade is worth taking is decision 30, below — a values call, not an engineering one.
The proposal was pitched in conversation before it was checked. Six lenses and two adversarial passes went at it. These three are wrong. The third is the one that matters, and it says the opposite of what was pitched.
L = D − A is the discrete analogue of −∇² — the kinetic term only. A discrete Schrödinger operator is H = L + V, where V is a diagonal potential: one number per node. The two coincide only when V = 0: the free particle. Everything that makes hydrogen hydrogen — the shells, the lobes in the video, the n/l/m structure — comes from the Coulomb potential. That is V, not L.
A bare Laplacian gives you clustering, not orbitals. The operator section below supplies the missing term, which makes the claim true instead of deleting it.
The multiplicity of eigenvalue zero counts connected components — the separate pieces of a graph — and nothing else. Two lenses ran it on live data. The meaning graph has 112 connected components over 143 nodes. The post-to-post graph has 15 edges over 114 posts: an average degree of 0.26, far below the percolation threshold of 1. Run today, “the eigenvalue answer to how many rails” is about 100, not 42.
On a truly empty graph it is worse, and worth stating as the honest limit: A = 0 gives L = 0, every vector is an eigenvector, and the layout is not bad but undefined. ARPACK returns different answers across runs and BLAS builds.
Backwards, and unanimously so — all six lenses, independently. Today position is unbuyable by construction: src/utils/spiralMath.ts:7 declares the invariant and the file imports nothing. railPoint(m, i) is a pure function of lineage, so there is no input path a buyer could touch. Position is not a hardened attack surface. It is not a surface at all.
Emergent position is a function of the edge set. Manufacturing edges is the entire link-farm threat model that Lantern, the ranking engine, already lists against rank (its Part B6). So this converts position from an axiom you verify by reading one 80-line file into a sybil-resistance property defended forever. (A sybil is one person behind many accounts.) It may still be the right trade. It is a downgrade, and it has to be taken deliberately, not sold as an upgrade.
Correction 3 · the attack surface, before and after
One caveat the adversary added, and it is fair. docs/dag-spiral-design.md already uses POSITION in two incompatible senses: §2, welded and unbuyable; §3 and §13, a locked-net capital score. And the deployed CategoryRegistry.getScore = TVL + volume30d (TVL is total value locked; the registry is one of seven contracts on the Base Sepolia testnet, none on mainnet) already decides which categories are in the visible 42. Money already ranks who is on the map.
Against that baseline, spectral position is an alternative to an already-scheduled capital-driven position, and the safer of the two. Correction 3 stands; the comparison is not against a perfect present.
The reference is a 13:52 C++ build of a hydrogen atom, done twice. The first version puts electrons on tracks. The second derives the shape from the equation and lets it arrive on its own.
Left: position assigned · Right: position sampled
struct Particle { vec2 pos; int charge; }, position as r = n * orbitDistance; angle += 0.05. It gets the energies right — −13.6, −11.51 and −3.39 eV are on screen. Then it is crossed out.
The hydrogen equation derived by hand — ψ(r,θ,φ) = R(r)·Y(θ,φ), the substitution ρ = 2r/na₀, reduction to the associated Laguerre equation — then implemented as sampleR(int n, int l, mt19937& gen), which builds a 4096-bin CDF once and inverse-transform-samples it for each of 100,000 points. (A CDF is a cumulative distribution function; inverse-transform sampling turns one uniform random number into one draw from it.) The n = 4, l = 3, m = 1 shapes are emergent: nobody placed a lobe.
The transferable lesson is the precompute/sample split, not the physics. The CDF is built once and offline; sampling is cheap and per-frame. That is exactly Lantern's slow clock and fast clock, and it is the part of the video that maps onto OuterTalk without any analogy at all.
“Your current DAG is the Bohr model” was the most flattering sentence in the session and it does not survive examination. The φ/ρ spiral is a closed-form a-priori law, which is structurally the Schrödinger side of the video, not the Bohr side. The real axis of difference is not imposed-versus-emergent. It is law-derived versus data-derived. The video's second atom is still law-derived; it fits no corpus. OuterTalk would be proposing something the video never did.
Sampling 100,000 points from a closed-form |ψ|² and laying out a 100,000-node graph look identical on screen and are nothing alike computationally. The first is independent sampling from a known function — embarrassingly parallel, O(N), zero data coupling. The second is an optimisation over an N×N coupled system. “Rendering 100k points is easy” is correct. “Therefore the layout is easy” does not follow, and it is precisely where the comparable projects got stuck.
The correction does not delete the idea. It supplies the piece that makes it literally true — and names where any editorial thumb would live.
V is a per-node scalar — exactly the shape of a thing money could buy. So naming V is a governance decision, not a rendering detail. The wall survives here because p is built only from human actions and never from a balance: money still sets brightness only. The personhood gate that p depends on is in design.
Setting γ = 0 at launch converts “not ready, hold it” into “ready, with the second term at zero” — a far better place to stand.
One operator · one eigenbasis · three physics
L generates three dynamics that differ only in the time factor. The wave line is the one that justifies the vocabulary: on ü = −Lu the eigenvectors are standing modes and ω = √λ, so “a category is a standing wave of the corpus” is a statement about the wave equation, not a flourish. λ is the squared frequency. Pick that convention and never mix it with the looser one.Kac asked in 1966 whether one can hear the shape of a drum. Gordon, Webb and Wolpert answered no in 1992, and isospectral non-isomorphic graphs — different graphs with identical eigenvalues — are common. The spectrum is a real property of the corpus but does not identify it. Never write that the spectrum is what the corpus is.
You need a graph with a spectrum, and there are 679 text units already. The vector extension, 0.8.0, is available and not installed. Embed title + body + poll options; build a mutual k-nearest-neighbour graph (k ≈ 8, Zelnik-Manor local scaling) — mutual, because plain kNN mints hub nodes and hubs are what produce a hairball — then union in the minimum spanning tree.
The MST union is not an optimisation. It is what guarantees one connected component, hence exactly one zero eigenvalue, which is the precondition for every other claim here. Without it you reproduce the 112-component collapse with a different edge source. Embedding the whole corpus is cheap enough that cost is not a reason to wait.
Four things survived every lens. The first is mathematics; the other three are design that follows from it.
On the graph wave equation ü = −Lu, solutions are u_k(t) = φ_k · cos(√λ_k · t). The Laplacian eigenvectors are the graph Fourier modes and the eigenvalues are the graph frequencies — standard graph signal processing, not analogy. Low-λ modes vary slowly across the graph; high-λ modes alternate between neighbours; the λ = 0 eigenvector is the constant vector, DC. So “a category is a standing wave of the corpus” is a mathematical statement about the wave equation on the citation graph. It is the single most honest bridge to the vibration-and-frequency interest, and it is the best sentence available.
Convention — pick one and never mix: ω = √λ, so λ is the squared frequency. Some sources loosely call λ itself “the graph frequency”. Both cannot appear in one document. In the same spirit, decision 31 drops the word “exact” from the superposition correspondence: signed eigenvector coefficients interfere; a non-negative PageRank vector, like Lantern's, does not.
The most valuable idea in the session, and it survives every lens. The registry holds the names; the spectrum may report a different number of resonances without renaming anything.
Best rendering of it: keep the 42 rails as a faint drawn overlay, give each post a spectral position, and tint it by its rail. Then “cluster 7 straddles rails 3 and 19” becomes a displayed finding about the world instead of a schema change. That is the most interesting screen in the product, it answers what the proposal's Claim 4 was reaching for, and it never touches the on-chain binding.
Spectral layout (Hall 1970; Koren 2003) has a unique global optimum and no local minima. But Laplacian eigenvectors localise on degree-heterogeneous graphs: one eigenvector gets spent separating a single hub and the rest collapses to a blob. The corpus is already heavy-tailed — 3 of 42 categories hold 116 of 182 tag edges. This is exactly what UMAP does, spectral init then local refinement, and it maps onto the two clocks: slow global solve on the server, fast local relaxation on the client.
The Serrano–Boguñá–Vespignani disparity filter, as a required spec step. It keeps only the edges that are statistically significant for the node they leave. It is the difference between this map and Obsidian's hairball.
This is the most important finding in Part E, and it changes the shape of the gate.
Use the Kesten–Stigum threshold: the point below which community structure in a sparse stochastic block model cannot be detected by any method (Decelle, Krzakala, Moore and Zdeborová, 2011). A stochastic block model is a random graph with planted communities. Reparametrise by ρ = 1 − f / f_random, where f is the fraction of a node's edges that leave its own category and f_random is what that fraction would be by chance. The required average degree collapses to a single expression.
| In-category share | Required citations per post |
|---|---|
| 40 % | 6.7 |
| 30 % | 12.5 |
| 20 % | 30.7 |
| 10 % | 164 |
c* = 1 / ρ² at 42 categories · against the measured 0.26
c* = 1/ρ² evaluated exactly at 42 categories, and 0.26 is 15 post-to-post edges over 114 posts, counted in production on 27 September 2026. The gap is about a decade and a half on a log scale, and adding posts at the same rate moves the rose line nowhere.Today's measured value is 0.26. At 114 posts, clearing the bar means 399 citation edges; at 2,000 posts, about 7,000; at 2,000 posts with a realistic 20 % in-category share, about 31,000. A post-count gate is the wrong instrument: it would pass while the method still cannot work.
Kesten–Stigum assumes equal blocks and Poisson degrees. OuterTalk's blocks are wildly unequal, which is strictly harder — so the realistic terminal state is “3 to 6 subjects emerge and 36 stay noise”, not “42 emerge”. And the plain Laplacian does not achieve the KS threshold on sparse graphs (the non-backtracking operator does), so with D − A the real requirement is higher still.
The precondition · and the measurement
L = 0, every vector is an eigenvector, and the layout is not bad but undefined.Part D already names the two defects that block it. Polls cannot cite at all — src/pages/CreatePost.tsx:1238/1393/1452 — and 102 of 114 posts are polls. Editing a post wipes its links: create_post_atomic assigns links = p_links with no COALESCE. Fix those and the question of whether the spiral should be computed becomes answerable with data instead of with physics.
Claim 10 of the proposal was “never store a post's position”. Verified: correct as a principle, and already fully satisfied. Every positional-looking column in production was enumerated. There is no stored layout coordinate anywhere. Every lat/lng is real geography. category_slot_scores.slot is a foreign key to categories.id. categories.depth is tree depth, and every row is 0. So there was nothing to reverse and no deadline; the urgency was misdirected.
The word “position” was hiding three objects, and the rule is true for exactly one of them.
| Object | What it is | Rule |
|---|---|---|
| ADDRESS | Permanent slot identity — what a token binds to. On the concept-token page this is Rule 8: “an address is earned at lighting”. | Must be stored, and immutable. |
| LAYOUT COORDINATE | An embedding under one projection, in one epoch. | Never authored. Cache only. |
| GEOGRAPHY | posts.lat/lng and friends. | Stored. Source of truth. Derived from nothing. |
src/components/SpiralDAG.tsx:65 sorts top-level rows by id. Line 77 uses the array index as the rail m. Line 68 sets railCount = Math.max(42, tops.length) and feeds it to theta0(m, railCount).
Therefore adding a 43rd top-level category re-spaces all 42 existing rails, and inserting a category into one of the ~60 gaps in the id sequence (42 rows spread over ids 1–102) silently re-addresses every rail after it. That is exactly the MOVE that §13A forbids, in the code today. The rail index is an array position, not an address.
SpiralDAG.tsx:68 · railCount = Math.max(42, tops.length)
m is m / railCount of a turn. Change the count and every angle changes. Nothing about the 42 categories changed; only a loop bound did. The binding meant to prevent this is populated on 1 of 42 rows.categories.blockchain_category_id is populated on 1 of 42 rows (History = 1), while docs/category-rule.md:5 states the 42 are “mapped to on-chain CategoryRegistry IDs (1–42)” and “locked”. That mapping does not exist in the database. The doc is wrong and should be corrected whatever else is decided. (The registry it names is one of seven contracts on the Base Sepolia testnet; none is on mainnet.)
category_slot_scores, category_slot_holdings and category_slot_events all carry ON DELETE CASCADE to categories. So the schema's answer to NO DELETE / NO MOVE is currently “delete and cascade the ledger”. Free to fix today: 0 holdings, 0 events, 42 score rows.
This is only a door once something persists a rail, and nothing does: the spiralDag flag is false, the card's onClick is empty, holdings are zero. A one-sentence note in docs/category-rule.md forbidding top-level inserts and deletes until the address is stored is free, is sufficient for now, and no app code writes that table.
The spec must name all six. Sign (v and −v are both unit eigenvectors). Ordering (near-equal λ swap). Degeneracy (inside an eigenspace of multiplicity k the basis is defined only up to O(k), which no per-vector sign rule can fix). Node-set change. Solver nondeterminism. Scale (unit-norm eigenvectors shrink as 1/√n, so the drawing collapses as the corpus grows).
The first three die to one orthogonal Procrustes alignment against an anchor set — the rotation that best maps one point set onto another: M = XᵀY, take its SVD, R = VUᵀ, apply before storing. Do not constrain det(R) = +1, because reflections are exactly the sign flips being absorbed. Anchors = the 42 category nodes. This is about 30 lines and it is the highest-value implementation detail here: without it the galaxy reshuffles on every recompute, and the failure is invisible in a screenshot because it only appears at the second epoch, weeks in.
The epoch lock · why the second recompute is the one that breaks
−v for v, to swap two near-equal modes, or to rotate inside a degenerate eigenspace. Each is the same layout to the solver and a different map to a person. One rotation fitted against the 42 category nodes — reflections allowed — undoes all three before anything is stored. The first epoch never shows the problem; the second always does.Six findings about the machine, each with its status.
100k instanced point sprites at 60 fps is routine WebGL2 from TypeScript: one VBO, one draw call. C++ compiled to wasm is justified only for the eigensolver, and only above roughly 50k nodes. At the 500-post corpus trigger, JS Lanczos is microseconds. Taking the C++ decision now buys a permanent renderer/web-app split for zero present benefit.
SharedArrayBuffer and pthreads need cross-origin isolation, which a capacitor:// origin does not get without changing the web origin. Build single-threaded and never pass -pthread.
mapbox-gl 3.12 has zero WebGL1 paths, and the Ark renders on device today. Lenses that flagged WebGL2 as a new risk were wrong. A separate question several lenses raised: IPHONEOS_DEPLOYMENT_TARGET, currently 14.0.
Two lenses gave directly contradictory answers about WKWebView, which is itself the finding: nobody should be writing that sentence.
That is Claim 9 of the proposal, restated honestly. The mechanism is Mapbox's CustomLayerInterface, which draws into the map's own WebGL context with the camera already synced. Worth a half-day spike before anything else; if it works, the unified field is nearly free and Mapbox stays.
The HCI evidence is genuinely on this side: discrete-outcome dot displays beat continuous densities for lay readers on mobile, and showing a summary statistic makes people ignore the spread entirely. This is the one piece of the hydrogen aesthetic with real evidence behind it — and it is the opposite of what a naive |ψ|² render would do.
The founder asked for a spiral where everything affects everything. That is precisely the property that makes the map re-seat itself on every recompute. Procrustes alignment is the machinery for suppressing what was asked for.
§13A's NO MOVE and “everything affects everything” are in direct tension. The resolution on offer — NO MOVE governs the address, the coordinate is a rendering of it — is a real resolution, but it is a choice, and it means the thing that moves is never the thing people navigate by.
Against about 10 required. The gate is this number, not the post count.
143 nodes. The MST union is what would make it one.
Embed the 114 posts by text similarity. Draw them once. Look.
Before any of the above: a throwaway script that embeds the 114 posts by text similarity and produces one image. No migration, no flag, no deploy, nothing to unship. If that image shows structure, the programme is worth planning. If it is a blob, the whole of Part E is answered for the cost of an afternoon — and the answer is that the corpus, not the renderer, is the subject.
Everything else in Part E is downstream of that picture.
Measured against production on 27–28 September 2026. Nothing on this page is built unless this table says so.
| Thing | State | Detail |
|---|---|---|
| Posts | 114 | 102 of them are polls, and polls cannot cite at all (CreatePost.tsx:1238/1393/1452). |
| Post-to-post citation edges | 15 · 0.26 per post | Counted 27 September 2026. The percolation threshold is 1. The gate is about 10. |
| Meaning graph | 143 nodes · 112 pieces | The method needs one piece. |
| Text units to embed | 679 | Titles, bodies, poll options. vector 0.8.0 is available and not installed. |
| Tag edges | 182 | 3 of 42 categories hold 116 of them. Already heavy-tailed. |
| People who have ever acted | 14 | The baseline any sybil defence would be measured against. |
| Top-level categories | 42 rows · ids 1–102 | About 60 gaps in the id sequence. categories.depth is 0 on every row. Zero child concepts exist. |
blockchain_category_id | 1 of 42 rows | History = 1. The 1–42 binding the category rule describes does not exist in the database. |
category_slot_scores / holdings / events | 42 · 0 · 0 | All three cascade on delete. |
| Stored layout coordinate | none | Every lat/lng is geography. Nothing persists a rail. |
spiralDag flag | false | The card's onClick is empty. |
| Rail index | array position | SpiralDAG.tsx:65/68/77. A 43rd top-level row re-spaces all 42 rails. |
| Spectral layout · H = L + V · epoch lock · rail overlay | unbuilt | Everything above the ledger is a specification. |
| Guard runner | unbuilt | Six guards exist. None executes until a runner does. |
| Renderer | mapbox-gl 3.12 · WebGL2 | Zero WebGL1 paths. IPHONEOS_DEPLOYMENT_TARGET 14.0. |
| On-chain registry | 7 contracts · Base Sepolia testnet | CategoryRegistry.getScore = TVL + volume30d decides the visible 42. No mainnet deploy exists. |
| Proof of personhood | in design | The gate that p in the operator depends on. |
Part E marks none of them decided. Each carries the panel's recommendation where it made one. Numbered so you can refer to one.
categories.slot_no (smallint, UNIQUE, immutable by trigger), backfill from today's id order, and read the rail from slot_no − 1 instead of the forEach index?Recommend yes.category_slot_* foreign keys from ON DELETE CASCADE to ON DELETE RESTRICT?Recommend yes — 0 holdings, 0 events.docs/category-rule.md:5, which claims an on-chain 1–42 binding that exists on 1 of 42 rows — and either backfill blockchain_category_id to match slot_no or drop the column?Recommend yes.category-rule.md forbidding top-level inserts and deletes?Recommend yes.H = L_sym + γ·diag(p) with γ = 0 at launch, making the physics claim literally true and converting the corpus gate from a hold into a dial?Recommend yes.CustomLayerInterface (half a day) before committing to any second renderer?Recommend yes.The week belongs to build 8 and to the governance_grades bridge index that must be dropped or extended by 30 September 2026 — two days out when this was written on the 28th, and unrelated to any of this.
The three items on the genuine critical path are already in Part D and are unchanged by Part E: the edit-wipes-links bug, polls-cannot-cite, and the guard runner. Part E's own most useful next step is the afternoon above, and it needs none of the above.
This is not open source. It is open to suggestions, from people who take it seriously. If a step in the mathematics is wrong, a threshold is misapplied, or a number here does not match what you can measure, we would rather hear it now than after it is built.