Lantern is the engine that will rank the Discover tab. It keeps a slow clock that remembers what people have vouched for, runs a fast clock over whatever a query touches, and never lets a credit balance reach the order of the results. Money changes brightness. People change rank. Position never moves. This is the design, written down before it is built, so that serious people can tell us where it is wrong.
The reason is not search engineering. Fourteen people have ever taken an action in OuterTalk. Only 12 of 114 posts can carry a link at all. There are zero citations anywhere in the database. So the order of work is: build the text search, hold the graph behind a corpus trigger, and say so out loud.
A few words, defined once. The map is the app's spiral of 42 category rails; the code calls it the DAG. Discover is the app's search tab; today it is four flat lists matched by substring, and posts are not in it at all. A node is a thing that can be found: a post, a note, a comment, a poll option, a topic, a URL, a domain, a place. An edge is a human action that points from one node to another — a reply, a note, an endorsement, a citation — and credit flows along it. Yon is the app's assistant, a service account, and it is never a person.
PageRank is a damped random walk over the whole graph, computed once, with no idea what you asked. HITS builds a ranking from the query's own neighbourhood: hubs, which point well, and authorities, which are pointed at. Lantern is neither. It keeps both ideas and puts them on separate clocks.
The wall is the rule that a balance, a stake or a price may never be an input to rank or to position. It is defined on the concept-token page. This page is what the wall looks like inside a search engine. A lantern lights what is already there. It never moves a star.
A damped random walk over what humans have replied to, noted, endorsed and cited. Runs offline every 6 hours. Restarted from 44 teleport vectors: 42 rails, one structural "open question" vector, one global. One human is one unit of weight. Stored as one 64-wide vector per node, so at query time the whole prior is a single inner product.
Text match forms a root set (top 60). One live hop over meaning edges forms a base set (cap 300). A relevance-damped, per-person-normalized hub/authority pass finds what this conversation converges on. Authorities enter the rank. Hubs — the notes and threads that point well — are shown as Guides, never fused into the answers.
A graph ranker over this graph would return its own starting assumptions. The offline walk would resolve to the teleport vectors it began with, and the citation channel would be a column of zeroes. That is not a reason to abandon the design. It is a reason to build it in two pieces and put a measured gate between them.
The corpus trigger · four counts against the gate · measured 26 September 2026
| What | Measured 26 Sep 2026 | Note |
|---|---|---|
| Distinct accounts that have ever taken any action | 15 → 14 people | Post, comment, note-vote, bet, grade, repost, favorite. One of the 15 is Yon's service account. |
| Distinct humans who ever posted / commented / voted on a note | 11 · 9 · 12 | |
| Posts that can carry a link at all | 12 of 114 | Only posts of type talk. The other 102 are polls. |
| Citations anywhere in prod | 0 · 0 · 0 | The links jsonb, posts.url, and http in body text. |
| Accounts already 30 days old | 34 of 35 | So the age ramp is not the gate. Participation is. |
| Rails with at least one human-authored post | 13 of 42 | |
| Searchable text | 679 units | Average post body 113 characters; 33 posts under 40 characters; 3 posts with question info or resolution criteria. |
| Distinct users who have ever searched | 2 | All 48 search-log rows come from the notes bar. The Discover bar has never logged a query. |
| Notes already clearing the 3-endorser activation bar | 5 | |
| Graph edges that would exist today | 2 · 13 · ~230 · 0 | Replies, reposts, tag edges, links. The prior would be nearly its teleport; the base set would be under 30, so the local pass would be skipped. |
| Distinct bettors ever | 10 | So every poll is "thin" on the money side and heat counts as zero. |
| Scheduled jobs failing silently | none | All 11 existing pg_cron jobs succeeding, newest run that day. |
| Post-to-post average degree | 0.26 | 15 edges over 114 posts; the meaning graph has 112 connected components over 143 nodes. |
Pure PageRank gives one global ranking that everyone shares. It is stable and cheap to read, but it has no idea what you asked. Pure HITS builds a ranking out of the query's own neighbourhood. It understands the question, but the neighbourhood is the ranking, so whoever gets into it controls the answer, and iterating it at query time does not hold a phone's latency budget as the corpus grows. Lantern runs both and keeps them on separate clocks.
The whole engine · two blue paths set the order · one gold path sets brightness · nothing writes position
| How the design was produced | Judge 1 · mechanism | Judge 2 · engineering | Judge 3 · product / App Review |
|---|---|---|---|
| 1 · Query-time HITS | 44 | 49 | 46 |
| 2 · Rail-sensitive PageRank | 47 | 54 | 48 |
| 3 · WALL-first three-channel | 48 | 52 | 47 |
| 4 · Adversary-first | 52 | 52 | 52 |
| 5 · Two-clock, Postgres-only skeleton | 49 | 57 | 54 |
| 6 · Camera engine (product) | 49 | 52 | 50 |
Six independent designs, three judges on seven criteria, one synthesis, three adversarial reviews (3 blockers, 23 majors, all three verdicts "needs repair"), a repair pass, and a completeness critic. 15 agents, 213 file reads. Skeleton: design 5. Grafted: design 4's wall constructions and anti-farm primitives; design 6's camera command and "answers inherit the question's Q"; design 2's closed teleport table and one-vector-per-node storage; design 3's hash-locked constants file; design 1's relevance damping and neutrality guard.
Direction is credit flows from source to destination. Human-action edges are one unit per distinct person, multiplied by that person's weight, and dropped when the origin and the target's author are the same person: no self-citation, self-reply, self-note, self-endorse. Structural edges are facts about a post, not votes by its author: they carry no person weight and no self-drop. No amount, balance, stake or tier is an input.
| Node kind | Source | Role in the walk |
|---|---|---|
| post | posts | Authority; a hub when it links or replies. Rail from its category. |
| note | comments · is_community_note or links | Primary hub: points at URLs, at its post, at a poll side. |
| comment | comments · the rest | Text-searchable; weak hub. |
| option | poll_options | Authority proxy for a claim until claim nodes exist (phase 3). |
| topic | topics · approved only | Authority when tagged; back-edges to posts so it never sinks rank. |
| url / domain | links · link_domains · new | Authority: the external record. A domain aggregates its URLs. |
| governance | governances | Authority; the "Near me" filter key. |
| slot (a category) | categories · 42 | Not a node. Rail membership is the teleport vector. Slot glow is money-side. |
| user | users | Not a node. Identity is used only for the same-person drop, the person weight, and the per-author cap. |
| claim | onchain_claims · phase 3 | Tables missing in prod. Replaces option. |
| Edge | From → to | Base weight | Notes |
|---|---|---|---|
| reply | post → parent post | 0.6 | One per person per parent. |
| comment_on | comment → post | 0.4 | |
| note_on | note → post; note → option | 0.8 | Side-scoped when the note names a poll side. |
| endorsed | post → its own note | 1.0 × Σ pw | Sum of the weights of distinct endorsers other than the author, collapsed by person. The row, never an amount. Corrected after the panel: the edge reads comment_votes where vote_type = 'endorsement' (45 rows; the button people press), not note_endorsements (0 rows; the money mirror, which has never been client-writable — a missing grant, not user behaviour). A "lacking" vote is read in phase 2 as a per-person −1 inside the hub term, never as an edge. |
| links_to | post or note → URL | 0.7 × verified × domain_age × act | See activation, below. |
| url_domain / domain_url | url → domain 1.0; domain → url 1/|urls| | structural | Phase 2. |
| tagged | post → topic 0.3; topic → post 1/|posts| | structural | Approved topics only. The back-edge makes topics authorities without sinking rank. |
| placed | post → governance 0.3; governance → post 1/n | structural | |
| repost | post → post | 0.5 | One per person. |
| cotag | topic ↔ topic | 0.3 × J | Derived nightly, Jaccard ≥ 0.1 over human-authored posts. Phase 2. |
| provenance | claim → post | 1.0 | Phase 3. |
| spun_from open | note → the poll it seeded | 0.8 | Proposed 29 September (decision 92): a human action by the Investigator who spun it. |
| located open | post → location 0.3; location → post 1/n | structural | Proposed 29 September (decision 92), exactly like placed. Dates stay a filter, not credit. |
Deliberately absent: pinned — the pin path requires the caller to be an active Investigator and the poll author and an admin settlement row, which is one person's choice twice over, so pins are a display affordance until settlement is crowd-ratified; follows, favorites, saves, views, searches and hides (private rows, personalization only); every money table; poll votes as edges (votes feed only the entropy term of Q); users as nodes.
| Person weight · pw | Value | Reads |
|---|---|---|
| Service account (Yon) | 0 | users.is_service_account |
| Account under 7 days old | 0.25 | users.created_at |
| 7 to 30 days | 0.5 | |
| 30 days and older | 1.0 | |
tier, business_verified, is_investigator | never read | One verified organization can mint unlimited verified accounts on day zero, and both flags are founder-granted. A guard forbids all three tokens in every meaning body. |
Three definitions the whole design reuses. human(u) is not a service account and pw = 1.0: the only predicate any head-count uses. A seven-day account weighs edges at 0.5 but is never counted as a human. person_key(u) is the organization id if there is one, else the user id: an organization and all its employee accounts are one person in every count. Phase 2 multiplies pw by rail diversity and an automatic ring penalty; phase 3 replaces the body with a personhood score. Today 34 of 35 accounts are already 30 days old, so the ramp gates nothing yet; participation does.
One query, end to end. Everything that decides order runs down the centre. Money joins at the very end, after the list is already sorted, and all it can do is make a tile brighter.
One query, end to end · order runs down the centre in blue · brightness joins last in gold
| Stage | Server | End-to-end p50 | p95 target |
|---|---|---|---|
| typeahead | 2–5 ms | 60–100 ms | ≤ 200 ms |
| Stage A · full search, no embedding | 5–20 ms today · 50–100 ms at 10⁷ posts | 100–150 ms | ≤ 300 ms |
| glow lookup · parallel, off the critical path | 2 ms | — | — |
| Stage B · semantic + local pass, phase 2 | +5 ms cache hit · +120 ms miss | 200–300 ms | ≤ 800 ms |
| Relevance / Open questions / Hot flip | 0 · client re-fuses the 120 | one frame | — |
| Near / Settled flip | one RPC call | 100–150 ms | ≤ 300 ms |
Two-stage paint: stage A paints in about 150 ms; stage B swaps in only once the user has been idle 300 ms and is not scrolling, so no result moves under a thumb. Phase 1 ships stage A only. Postgres only. The text index is a side table, never a generated column on posts, because 14 client paths select every column and a tsvector would double every feed payload. At 10⁷ posts: monthly partitions, per-partition indexes, and a named PageRank worker at 10⁶ edges. Nothing in the equations changes; only where the arithmetic runs.
This is the argument made checkable. Every symbol is tinted by the channel it belongs to. If a gold term ever appears inside the score that orders results, the wall has been breached, and you can see it without reading the code.
meaning — people, and only people money — brightness, and only brightness structure — fixed constants
glow · what 100× the stake buys · the money channel is damped by a square root
Each lane produces numbers on its own scale. A text-match score and a random-walk probability are not comparable, and if you add them the lane with the bigger numbers silently wins. So Lantern throws the scores away and keeps only the position in each list. That is reciprocal-rank fusion. The constant k = 60 then decides how much being first is worth.
1/(k + rank) · how much a lane's vote falls off with position · k = 60 against k = 1
k constant is the anti-tyranny knob. At k = 60 a lane that puts something twentieth still contributes three quarters of what a first place contributes, so agreement between lanes matters more than any single lane's enthusiasm. At k = 1 the first result is worth ten times the twentieth and whichever lane shouts first wins.The wall is defined on the concept-token page: a price may never change a name, a position or a truth status, and no evidence process may ever read a price. Inside a search engine it means three columns, two barriers, and four tests that read the SQL and fail the build on a forbidden word.
Three channels · money sets brightness · meaning sets order · position is set by nothing here
amount is absent.| Guard | What it asserts | Status today |
|---|---|---|
| search-wall | No meaning function body contains a money token. The glow function touches no meaning table. No ORDER BY in a meaning body mentions glow. Every Lantern function has extensions in its search path, and every trigger function is SECURITY DEFINER. | no runner |
| search-neutrality | The question-ness function contains no topic name, no category id, no string literal. Two structurally identical nodes with opposite content score identically to 1e-9. A golden case pins SQL and the TypeScript mirror to the same numbers. | no runner |
| search-constants | Every weight in the lock file appears verbatim in the SQL, and the hash matches. | no runner |
| search-position | No search code references the geometry helpers. The geometry file imports nothing. The map component never imports the search client. | no runner |
| Attack | Defense |
|---|---|
| Sybils (free signup, 10,000 ✦ each) | Age ramp; only 30-day accounts are humans in any count; one unit per person per target; organization collapse; phase 2: rail-diversity factor and a nightly co-action ring job (pairs acting on the same targets within 60 minutes 5 or more times; dense young components get ×0.1 for 60 days, automatic, with a sunset, no appeal path); phase 3: personhood. |
| Organization-minted accounts | The flags are never read; an organization and its employees collapse to one person. |
| Link farms, fabricated citations (the X pipeline fabricates 97% of its drafts) | Reachability check; activation by 3 other persons; young domains at half weight; Yon links weigh 0; phase 2 structural domain seeds: a domain seeds trust only when cited by ≥ 5 humans from ≥ 3 rails over ≥ 90 days, recomputed nightly, no hand-picked table. |
| Tightly-knit community capture (HITS's failure) | Base set ≤ 300 seeded by text; relevance floor 0.1; per-person normalization; the global prior in the same fusion; "small circle" copy with no re-rank. |
| Topic drift (HITS's other failure) | Relevance multiplies every column of the adjacency; in-hub sampling by global rank; S damped ×0.5 for nodes with no text match; no hard rail wall. |
| Rich-get-richer (PageRank's failure) | d = 0.85 with 44 teleport vectors; EMA between refreshes; the prior enters as a rank, not a magnitude; a freshness lane at 0.4. |
| Wash heat, whale glow | Per-person netting over 24 hours, square root, zero under 10 persons, 15% slot cap at ≥ 10 backers, 24-hour half-life; heat reaches only glow and the opt-in Hot sort. |
| Yon self-citation (70 of 114 posts are Yon's) | Yon's weight is 0 on every human-action edge it originates; Yon posts stay text-searchable, get the rail's provisional prior, and gain authority only from human edges into them. |
| Status gaming ("everything is a mystery") | Self-declared = 0; 10 humans, 3 rails, 60%, a 7-day challenge window; under 60% = contested; Yon's proposal is not read. |
| Fetching a link (SSRF, resource exhaustion) | http and https only; private, link-local and loopback ranges rejected after DNS; max 3 redirects; 64 KB read cap; 10 s timeout; 1 request per second per domain; a per-user daily link quota. |
| The founder editing weights | Lock file + migration + changelog + hash guard; the ratification RPC treats the admin as one row; no admin write path to the score tables. |
The thesis is that money should arrive first and pull information behind it, on repeat. Search is what closes that loop, because it is the surface where a rail that has been paid for but never sourced becomes visibly hollow.
The repeated cycle · backing buys brightness · brightness buys attention · attention writes the only edges that move rank
Dark stars — high S, glow at the floor — are the on-ramp: meaning nobody has backed. Their tile carries "Back this slot" after review, so money flows to under-lit meaning first. Information debt is routed, not hoped for: a function returns, per rail, slot glow against the sum of Q and prior, and "backed, needs sources" rails go to Yon's draft queue and to Investigators by beat. Search demand per rail (humans only, at least 10 distinct per rail, query-cluster and week, shown as a range) appears beside the slot standings and is never added to any score. Repeated unmet searches in a rail with no subcategory are the "verified demand" a new slot needs. Search never mints, pays, refunds or reorders a slot.
The goal is that consciousness, the layer under the Standard Model and the structure of the universe outrank the news cycle. The constraint is that nothing in the ranker may name a subject, because a founder-editable list of favoured topics is both a mission violation and an App Store review risk. The resolution is to rank the shape of a question rather than its content. Those questions win because they are open by the crowd's own ratification, they split any crowd, they generate deep sourced chains, they touch many rails at once, they stay alive for years, and belief about them runs far ahead of evidence. A question about next month's interest rate scores well for three weeks and then settles.
Q · the six weights · decision 11, recommended yes
The app's thread Tree view and Lantern are not tied together today. They should be, and the fix is smaller than it sounds, because both already eat the same raw material. The Tree view is the viewer for Lantern's B-set. The spec already defines B = R ∪ 1-hop over the live meaning-edges view as the neighbourhood a query is ranked in. A thread is just R = {root} and B walked along reply.
| Surface | What it reads today | Via |
|---|---|---|
| Tree view spine | Posts where id = root or thread root = root, chained by posts.parent_post_id (falls back to root) | The thread loader and the tree layout helper |
| Tree view branches | posts.source_comment_id (a poll spun off a community note); pinned consensus notes; fork colour gold / teal / grey = the fork's own vote with a quorum | Display only; cross-Investigator branching is scaffolded, inactive |
| Connections chips | category_id, related categories, governances, topics, locations, plus event dates on the posts | The batch tag-name loader |
| Lantern edges | reply = posts.parent_post_id 0.6 · tagged = topics · placed = governances · rail membership = category + related categories · note_on, endorsed, links_to, repost | The meaning_edges view — exists only on paper (Migration B) |
So the tree's spine is Lantern's reply edge, and every Connections chip is a Lantern structural node. They are two code paths over one graph that nobody has built yet. What Lantern does not have that the thread has: source_comment_id (no spun-from edge), locations (no located edge; the spec places by governance only), event dates (not in the graph at all), and pinned notes (deliberately absent; the pin path is founder-gated). Today the tree stops at its own thread root; walking B instead gives it, for free, the cross-thread replies, reposts and citations that phase 2 "cross-Investigator branching" was going to need its own backend for. One backend, not two.
One graph, three views · today the tree walks reply inside one thread · proposed: it reads B
spun_from, which is decision 92. Off-thread hops render as the teal and grey off-rails the tree already knows how to draw.meaning_edges as a view first — the data-plumbing half of Migration B. It reads only tables that exist. It is safe before the corpus trigger because a view ranks nothing.spun_from (note → the poll it seeded, 0.8, a human action by the Investigator who spun it) and located (post → location 0.3, location → post 1/n, structural, exactly like placed). Dates stay a filter, not credit — time is a frame, not an endorsement.reply, spun_from, repost, links_to; off-thread hops render as the teal and grey off-rails the tree already draws.parent_post_id is a freely editable pointer any author can re-aim. It is the tree's spine and the reply edge. Seal it once.Order: step 1 now (cheap while the tables are small, and it is a view); 2 with it; 3 to 5 after the site work; 6 whenever rank exists; 7 in this week's guards. The corpus trigger still gates the ranking; nothing above needs rank to be useful.
spun_from and located to the edge table. Recommend yes. openpinned display-only until the pin path is crowd-ratified. Recommend keep. openparent_post_id with the other guards this week. Recommend yes. openEverything to the left of Migration A is cheap right now precisely because the tables are empty, and gets expensive the moment a graph depends on them. Everything to the right of the diamond waits for a number, not a date.
Phase 0 · 14–18 founder-days, solo · then Migration A · then a measured gate
| Phase | What | Estimate |
|---|---|---|
| 0 · readiness | The prerequisites that are only cheap while the tables are empty: the link-edit data-loss fix, a unique index on the endorsement table, sealing the reply pointer, a shape check on post links, one canonical URL helper, the guard runner plus the four corrected guard contracts, the measurement instrumentation, and the one-way key decisions. Carries one integrity migration — phase 0 is not migration-free. | 14–18 days, not 2 |
| 1A · build 9 | Migration A only: extensions, graph nodes minted by trigger, the text side table with SECURITY DEFINER sync triggers, person weights, the constants function, search v0 (lexical + trigram + freshness, author cap, hide mask, a why payload). Client: search tile, one ranked list in Discover, Relevance and Near chips, typeahead, why bubble, cold-graph copy. This is where all the user-visible gain is. | ~2.5 weeks solo |
| gate | The corpus trigger. Do not build 1B until the graph has mass: 500 posts · 100 human comments · 30 distinct 30-day actors · 50 activated links. Today: 114 · 29 · 14 · 0. | measured, not scheduled |
| 1B | Migration B (the meaning-edges view, graph edges, links, domains, 44 teleport vectors, rank vectors, question scores, glow, the caller's rail vector, the refresh functions, cron with a heartbeat row per job) and Migration C (search v1 with the live 1-hop and the local pass, the glow reader, the rail-debt function); the link verifier on a shared safe-fetch helper. | ~3 weeks solo |
| 2 | Embeddings (512-wide, HNSW, a query cache, two-stage paint); the ratification migration; ring job, rail diversity, endorsers'-rail reach, bridging; then the Open questions chip, Settled chip, Open Mysteries room, galaxy lens, Guides row, "Ask the Outernet", demand panels; a 100-query labelled set; the post-star layer and camera handle. | 4–5 weeks |
| 3 | Claim nodes once the claim-ledger tables reach prod; the personhood swap; the calibration decision; triggers replace the nightly rebuild; the PageRank worker at 10⁶ edges; partitions; chain glow through the existing seam. | scale-driven |
There is no client contractor. Every outside artefact in the repo is iOS build work, so these are solo, serial estimates (about 29 founder-days for phase 1). Migrations are applied by hand, one at a time: prod has 203 applied, newest 20260923170924, and the push tool fails on a version-string mismatch, not a gap in history. They sequence after build 8 ships and the blocked billing migration is decided. Migration A's trigger is the sharpest applied risk in the set — there is no insert trigger on posts or comments today, both are written directly by every installed binary, and a raise inside the new trigger is an app-wide posting outage; the body must swallow every failure and the one-line disable belongs in the file header. Nothing watches the cron jobs; the precedent is a share-card function registered in April and returning 404 on every share for five months unnoticed. Decide the heartbeat before B.
| Do now, while it is free | Why it is free today | Cost |
|---|---|---|
| Stop post edits wiping their own links live bug | The edit composer starts with an empty link list and never loads the post's existing links; on save it sends null, and the update assigns it with no guard. It has never fired — there are 0 link rows, so there is nothing to repair yet. | 1–2 h |
| Unique index on the endorsement table | The key is on the row id alone; uniqueness is a read-then-write in the client. 0 duplicate groups exist today; after a graph depends on it, a dedupe also invalidates the prior. | ~1 h |
| Seal the reply pointer | Any author can re-aim a reply at any post, and the thread-purity trigger fires only on insert. The 0.6 reply edge inherits all of this. | ~2 h |
| Cap and shape-check post links on the server | The comments table has a max-4 check; the posts column has none. The cap of 4 is client-only. | ~30 min |
| One canonical URL helper | Three copies of the domain parser are wrong the same way (an unanchored replace turns newww.com into necom), before anything keys on a domain. | ~1 h |
| Start logging searches and result taps | This is the only item whose cost is calendar; it cannot be backdated. | ~1 d |
The design ranks on citations. The product barely lets anyone make one, and that is a product problem rather than a ranking problem.
The Discover bar has never logged a query. All 48 search-log rows come from the notes bar, a different surface with a different intent, from 2 distinct users, and they are keystroke prefixes, not intents: logged while typing with a 250 ms debounce and no submit boundary. No result tap is logged anywhere, so the outcome half of every search is missing. The zero-result rate cannot be reconstructed later. So: log on submit with two arguments only — never a matched category id, which would switch on a dormant weight in the category ranking and move the live category bar under the measurement; add a small opens table and wrap the four tap handlers; freeze a fixture of today's results as the before number; hand-write the labelled query set from real content. Also on the record: a 1.2× Smart Poll boost is live in the default Home feed today. It reads a post-type boolean, never a balance, so it is not a wall breach, but it is a flat prominence boost on the content class that carries a pool, and it should be a written decision before the baseline is frozen.
The design describes a search as a camera command: light the stars that match, dim the rest, draw the arcs between them. That is the right destination and it is not buildable today, so it should not be described as though it were. The renderer draws 42 category bands, 42 cards and a ring of decorative dots. There is no star for an individual post, no column anywhere that would give a post a position along its rail (the sub_index the RPC names exists nowhere in code or schema), and no depth axis in use. The display radius is not the geometry helper's radius. Nothing in the app can move the viewport programmatically. The rail index is an array position, not a stored address, so adding a category rotates every future star; the stable address must be decided before anything persists a rail. Geometry is the subject of the second-atom page.
Measured against production on 26 September 2026; the Tree-view rows are dated 29 September 2026. Nothing on this page should be read as shipped unless it is in this table.
| Thing | State | Detail |
|---|---|---|
| Discover tab | exists · four flat lists | Substring match over four groups. Posts are absent from it entirely; topics are filtered to approved. It has never logged a query. |
| Lantern ranking engine | in design | Spec final as of 26 September 2026. Nothing built, committed or applied. Even the name is decision 16. |
| Lantern tables | 0 exist | About 12 sealed tables and 5 cron jobs are specified. The meaning_edges view exists only on paper. |
| Guard tests | 4 specified · no runner | One hook file, no CI, no root instructions file. Three contracts to fix before any file is written. |
| The corpus trigger | 114 · 29 · 14 · 0 | Against a suggested 500 · 100 · 30 · 50. Not decided. |
| Citations per post | 0.26 | Against a proposed ~10 sustained (decision 42). 15 post-to-post edges over 114 posts. |
| People who have ever acted | 14 | 15 accounts; one is Yon. |
| Posts | 114 · 12 can link | 102 are polls, which cannot carry a link. 18 have no rail. 70 are Yon's. |
| Citations | 0 | In the links column, the URL column, and body text alike. |
| Endorsements | 45 rows · 0 rows | comment_votes endorsement rows, the button people press; note_endorsements, the money mirror, never client-writable. 5 notes already clear the 3-endorser bar. |
| Edges that would exist | 2 · 13 · ~230 · 0 | Replies, reposts, tag edges, links. |
| Distinct bettors ever | 10 | Every poll is thin on the money side. Slot rows are 42 seed values with 0 holdings. |
| Accounts 30 days or older | 34 of 35 | The age ramp gates nothing yet. |
| Rails with a human post | 13 of 42 | |
| Search logs | 48 rows · 2 users | All from the notes bar; keystroke prefixes. No result tap logged anywhere. |
| Editing a post wipes its links | live bug · never fired | 0 link rows, so nothing has been lost yet. Free to fix now. |
| Scheduled jobs | 11 · all succeeding | Nothing reads the job-run table; a heartbeat is owed before Migration B. |
| Migrations applied in prod | 203 | Newest 20260923170924. Hand-applied, one at a time. |
| Vector extension | 0.8.0 available | So 512-wide half-precision vectors and iterative scan need no workaround. |
| The renderer | flat 2D · 42 rails | Pan and zoom. No per-post star, no sub-slot column, no depth axis, no camera handle. Brightness is the only data channel. |
| Tree view ↔ Lantern | not tied · 29 Sep | Two code paths over one graph nobody has built. Decisions 91–94 open. |
| Decisions | 27 + 42 + 91–94 · none made | The 27 the design needs are in Part C of the plan; 42 and 91–94 were added after. |
Numbered as the spec numbers them, with the spec's recommendation. None has been made. Decisions 21 to 27 are in Part C of the plan and cover, among other things, post likes and reply hearts, place grades (158 rows, the largest human signal in the app, kept out because it grades a place rather than content), comment-on-comment replies, and the English-only text config. Tell us which way, and why.
This is not open source. It is open to suggestions, from people who take it seriously. If a lane is gameable, a guard is hollow, or the wall has a door we did not draw, we would rather hear it now than after it is built.