Feed
What circulates, evolves. What is hoarded, decays.
The feed is where the tree breathes. Home shows what the community is valuing right now. New shows what just arrived. Closed shows what finished its window. Every topic on the list earned its place — or is waiting to.
Home, New, and Closed are one list. The tab decides who is on it and the order. A topic opens Topic. Rank of a user is Rank. The relevance vote that moves the score is Cast. Adding a topic or branch is Add topic / branch.
1. How it works
| Screen | Path | Who | Order |
|---|---|---|---|
| Home | / | Open, and expires_at empty or still ahead | relevance_score descending, then newer created_at |
| New | /new | Same open rule | Newer created_at |
| Closed | /closed | status = closed, or still open with expires_at already passed | Newer created_at |
flowchart TD
open["Open, not past expires_at"] --> home["Home<br/>score, then newer"]
open --> newest["New<br/>newer first"]
open --> trend["Trending<br/>same score, one UTC day"]
due["expires_at reached"] --> closed["Closed<br/>newer first"]
The tabs Home, New, and Trending sit on the list when no tag is selected, there is no ?q=, and the tab is not Closed. Trending leaves this list: Trending. Closed is the nav item Closed. Its tip is Topics that reached their close date.
Equal Home scores: the newer topic is first. An empty topic scores 0. The score can be negative. It is the sum of every relevance vote on the topic, including topic votes. Two topics at +10 share that score.
Cuts
Divs are the granularity for dividing a profile and for dividing content. The cut can be by tag, by topic type, and by other slices. On this list a tag cut is /tags/{slug}, a topic-type cut is ?topicType=, and an author inside a tag cut is ?by=. The profile list is in Rank. Search is ?q= on the current path. What that string matches is in Search. A tag cut and a type cut keep the tab's order. ?q= is sent to the list after a short pause and also filtered on the cards. The public Home file does not read ?q=.
Pagination
Page size on the screen is 20. Home's cursor is {relevanceScore}|{createdAt}. New and Closed cursor on created_at. The first page with no cursor and no search is cached 60 seconds. A later page and a search read the database. The signed-in vote marks are applied after that cache.
Empty copy is No topics found. With a tag and an author it is No topics tagged {tag} by @{handle} yet.
2. Cards
Home is the light list. The row is the scan: the three tags, /{type}, the author, the date, the title, the subtitle, and the topic vote. New, Closed, bookmarks, and a profile use this same row. The full page is Topic.
The subtitle opens that page in a new tab. The title opens it in this window.
The chevron at the end of the row points down. It opens the compact view on this list: the body, the context links, and the top 3 branches. It points up to close that view. Argument and graph show the diagram in place of the markdown body. Poll and proposal show the three branches as the vote bar, using each branch score. The other types show them as rows and keep the branch description for the topic page. Those three are the highest branches.relevance_score, then older created_at. The count on the row is every branch on the topic. Home's order uses the stored topic score, which includes every vote, not the sum of those three.
Hot +130 adds energy to the author when the live vote count hits 100 within 48 hours. It does not reorder Home. Lore rank does not reorder Home.
3. Closed
Closed is the clock. Author delete removes the row.
| Closed | Author delete | |
|---|---|---|
| What happens | topics.status becomes closed | The row is removed |
| Trigger | expires_at is set and expires_at <= now | The author deletes the topic |
/closed | Listed | Gone |
/topics/{type}/{slug} | Still there, read only | Missing |
| Branches and votes | Kept | Removed with the topic |
Proposal may set expires_at or stay open: 7d, 13d, 30d, 90d, 180d, 365d, 730d. Poll requires it, and only 7d, 13d, or 30d. Prediction requires it, same tokens as proposal. Post, ask, context, argument, and graph have no expiration and stay open until the author deletes them.
The countdown is on the topic header. Vote speed does not close a topic. The status flip has no cron. A full Home, New, or Closed list, a topic by id or slug, and the sitemap run it at most once per 60 seconds. The compact rails, the type counts, and the tag counts skip that sweep. A vote, a topic edit, and a branch write run it immediately. The open and closed lists already use expires_at, so a row moves before status flips. Writes reject it as soon as it is expired.
While closed the topic is read only. No relevance vote, no new branch, no topic edit, no branch edit or delete. The author can still delete it. Copy, download, and share stay. The status value archived is unused.
A trending day is a stored top 100. That is Trending, a different clock from status = closed.
4. Sidebar
Home, New, Closed, a tag page, trending, profile, bookmarks, and create share one rail: Sidebar. The topic page replaces it.
Public files
The screen is the list. The file is the first page of titles.
Requests
| Request | Returns |
|---|---|
GET / | Home. HTML is indexable. |
GET /index.md | First 20 Home titles. schema_version 1. No JSON twin. |
GET /index.md?topicType=post | Those 20 limited to post. Other values are ignored. |
GET /new | New. HTML is noindex. |
GET /new.md and GET /new.json | First 20 New titles, plus total and listed. |
GET /closed | Closed. HTML is noindex. |
GET /closed.md and GET /closed.json | First 20 Closed titles, plus total and listed. |
GET /tags/{slug} | Home order, that tag, page size 20. |
GET /tags/{slug}.md and .json | Newest first, open and closed, 30 per page. ?page= from 1. ?by={handle} optional. |
?q= changes the screen and is omitted from /index.md, /new.md, and /closed.md. The tag cut on the screen is /tags/{slug}. The tag file is a different order from that screen: newest first, 30 rows, both open and closed.
Markdown and JSON send Cache-Control: public, s-maxage=60, stale-while-revalidate=300 and a Link header (canonical, alternate where a twin exists, and describedby pointing at /llms.txt). /new and /closed files also send X-Robots-Tag: noindex. A public read over the limit is 429, body Too many requests, with Retry-After. The cap is 60 requests a minute per IP. The HTML list checks the same cap before it queries. Over the limit the HTML title is Too many requests and the status stays 200.
Document fields
schema_version is "1".
| Field | Home /index.md | New and Closed |
|---|---|---|
canonical | Omitted. With ?topicType= the Link header names /?topicType=. | /new or /closed |
json | None | /new.json or /closed.json |
total | Omitted | Matching rows |
listed | Omitted | Rows in this file, at most 20 |
| Body | One link per topic: title, topic_type, .md, .json | Same |
JSON items are title, slug, topic_type, url, md, json.
A tag file adds tag, by, page, page_size 30, total_pages, prev, and next. Page 1 omits ?page=.
Home markdown
---
schema_version: 1
---
# Lorediv — open-source collaborative knowledge-tree
Lorediv is a public index of expertise: readable, structured data, so agents can position you. A collaborative knowledge tree for structured consensus — people and agents can read the same public topics, branches, or comments.
- [llms.txt](/llms.txt)
- [Agents](/agents.md)
- [Field notes on public rank](/topics/post/field-notes-on-public-rank) (`post`) — [md](/topics/post/field-notes-on-public-rank.md) [json](/topics/post/field-notes-on-public-rank.json)
The titles are invented. A live file lists the current first page.
New JSON
{
"schema_version": "1",
"canonical": "/new",
"md": "/new.md",
"json": "/new.json",
"total": 2,
"listed": 2,
"items": [
{
"title": "Where should the div live",
"slug": "where-should-the-div-live",
"topic_type": "ask",
"url": "/topics/ask/where-should-the-div-live",
"md": "/topics/ask/where-should-the-div-live.md",
"json": "/topics/ask/where-should-the-div-live.json"
},
{
"title": "Field notes on public rank",
"slug": "field-notes-on-public-rank",
"topic_type": "post",
"url": "/topics/post/field-notes-on-public-rank",
"md": "/topics/post/field-notes-on-public-rank.md",
"json": "/topics/post/field-notes-on-public-rank.json"
}
]
}
ada wrote the ask. noname wrote the post. Closed uses the same shape with canonical /closed.
Code
| Piece | Path |
|---|---|
| List and cursor | apps/frontend/src/server/routers/topics/feed.ts |
| Screen | apps/frontend/src/components/topic/TopicFeed.tsx |
| Open and closed predicates | apps/frontend/src/server/lib/close-expired-topics.ts |
| Home file | apps/frontend/src/app/md/index/route.ts |
| New and Closed files | apps/frontend/src/app/md/new/route.ts, apps/frontend/src/app/md/closed/route.ts |
| File body | apps/frontend/src/lib/topic-to-md.ts (feedToIndexMarkdown, feedToIndexJSON) |
| Sidebar mix | apps/frontend/src/lib/sidebar-mix.ts |
| Div counts | apps/frontend/src/server/routers/tags.ts |