Add related articles under web issue articles and keep embeddings forever

Each pick's web page now lists up to five related articles: the nearest
embedded articles across the whole archive (cosine >= 0.55), excluding the
rest of the issue, excerpt-only articles and same-title copies. They are
computed once per issue after publishing and stored in issue_related
(migration 0014), since the full embedding scan is too slow per page view;
`features related [--date]` and the related-refresh job backfill older issues.

Related articles that ran in an issue link to that issue page; the rest link
to a new signed-in /articles/{id} page rendering the stored body with no
summary. /articles/{id} for a published article redirects to its issue page.

embedding_retention_days = 0 now means keep every vector (previously it would
have deleted every unrated, unpublished embedding) and is the new default, so
related lists can reach the whole archive.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01AeJmv3WED4w8S3GV7mMjS7
This commit is contained in:
2026-10-11 21:34:31 +00:00
co-authored by Claude Opus 5.5
parent bf6a8d0129
commit 03f0a454be
17 changed files with 788 additions and 30 deletions
+12
View File
@@ -0,0 +1,12 @@
-- Related articles under each web issue article: the nearest embedded articles
-- across the whole archive, computed when the issue is published (and by
-- `features related` for older issues). Stored rather than computed per page
-- view because finding them is a scan of every cached embedding.
CREATE TABLE issue_related (
issue_date TEXT NOT NULL REFERENCES issues(date) ON DELETE CASCADE,
article_id INTEGER NOT NULL REFERENCES articles(id) ON DELETE CASCADE,
position INTEGER NOT NULL, -- 0 = closest
related_id INTEGER NOT NULL REFERENCES articles(id) ON DELETE CASCADE,
cosine REAL NOT NULL,
PRIMARY KEY (issue_date, article_id, position)
);