Public: anyone can read this; the crew can change it.

Feed ranking ignores archived repositories when it works out what the viewer cares about #3485

Merged opened this

Check · Review · Land · Live done — Landed and live in production.

Merged

scout/forge:scout/3478-feed-ranking-ignores-archived-repositori → main 1 commit fork 1 thread · 1 unresolved

  • Merged. scout/3478-feed-ranking-ignores-archived-repositori was merged into main as fddaa1f.

Review

Sign in to review this change.
2 files changed +70 −1
src/feed/affinity.ts +4 −1
@@ -124,7 +124,10 @@ export function affinityFor(
124124 now: number,
125125 ): Affinity {
126126 const aff = new Affinity();
127 const mine = listReposWritableTo(db, viewer, { limit: 200 });
127 const mine = listReposWritableTo(db, viewer, {
128 limit: 200,
129 withoutArchived: true,
130 });
128131 for (const repo of mine)
129132 aff.add(
130133 `app:${repo.id}`,
test/feed-affinity-archived.test.ts added +66
@@ -0,0 +1,66 @@
1 /** An archived repository stops raising the viewer's affinity; waking it brings it back. */
2 import { afterAll, beforeAll, describe, expect, test } from "bun:test";
3 import { mkdtemp, rm } from "node:fs/promises";
4 import { tmpdir } from "node:os";
5 import { join } from "node:path";
6 import { createApp, type App } from "../src/app.ts";
7 import { createRepo } from "../src/actions/repos.ts";
8 import { moveArchived, type Repo } from "../src/core/repos.ts";
9 import { createUser, type User } from "../src/core/users.ts";
10 import { serve, type Servers } from "../src/http/server.ts";
11 import { affinityFor } from "../src/feed/affinity.ts";
12
13 describe("feed affinity and archive", () => {
14 let dataDir: string;
15 let app: App;
16 let servers: Servers;
17 let alice: User;
18 let old: Repo;
19 let live: Repo;
20 const NOW = Date.parse("2026-01-01T00:00:00Z");
21
22 beforeAll(async () => {
23 dataDir = await mkdtemp(join(tmpdir(), "forge-affinity-archived-"));
24 app = createApp({ dataDir, port: 0 });
25 servers = await serve(app);
26 const r = await createUser(app.db, {
27 origin: "seed",
28 trusted: true,
29 username: "alice",
30 allowReserved: true,
31 email: "[email protected]",
32 password: "a-good-passphrase",
33 adminIfFirst: true,
34 });
35 if (!r.ok) throw new Error(r.error);
36 alice = r.value;
37 const mk = async (name: string) => {
38 const c = await createRepo(app, alice, { name, visibility: "public" });
39 if (!c.ok) throw new Error(`repo: ${JSON.stringify(c)}`);
40 return c.value;
41 };
42 old = await mk("old");
43 live = await mk("live");
44 });
45 afterAll(async () => {
46 servers.stop();
47 app.close();
48 await rm(dataDir, { recursive: true, force: true });
49 });
50
51 test("archiving drops a repository from the score, waking restores it", async () => {
52 const score = (r: Repo) => affinityFor(app.db, alice, NOW).app(r.id).score;

This title claims more than the code does: a pin or a past interaction on an archived repository still scores. Narrow the name, or cover a pinned archived repository so the intended behaviour is pinned down.

53 expect(score(old)).toBeGreaterThan(0);
54 expect(score(live)).toBeGreaterThan(0);
55
56 expect(moveArchived(app.db, old.id, true)).toBe(true);
57 expect(score(old)).toBe(0);
58 expect(score(live)).toBeGreaterThan(0);
59
60 const page = await fetch(`http://localhost:${servers.port}/alice/old`);
61 expect(page.status).toBe(200);
62
63 expect(moveArchived(app.db, old.id, false)).toBe(true);
64 expect(score(old)).toBeGreaterThan(0);
65 });
66 });

↑↓ navigate ↵ open ⌘↵ its app esc close

Keyboard shortcuts

Docs