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

Archive, part 2a-ii: the terminal home leaves archived repositories out of Yours and On you #2500

Merged opened this

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

Merged

scout/forge:scout/2494-archive-part-2a-ii-the-terminal-home-lea → main 2 commits fork 1 thread · 1 unresolved

  • Merged. scout/2494-archive-part-2a-ii-the-terminal-home-lea was merged into main as c59bf6f.

Review

Sign in to review this change.
3 files changed +42 −2
src/core/repos.ts +4
@@ -1053,6 +1053,10 @@ export interface RepoSettings {
10531053 plansNeedNod?: boolean;
10541054 }
10551055
1056 /** The repositories still awake: what Yours lists before anyone types a filter. */
outdated

This doc comment described ARCHIVED_WHY, but withoutArchived now sits between them, leaving two stacked doc comments. Move the helper above this comment.

1057 export const withoutArchived = (repos: readonly Repo[]): Repo[] =>
1058 repos.filter((repo) => repo.archivedAt === null);
1059
10561060 /** The one line said wherever the crew declines to start a run here. */
10571061 export const ARCHIVED_WHY = "This repository is archived; wake it to start a run.";
10581062
src/tui/screens/dashboard.ts +6 −2
@@ -48,6 +48,7 @@ import {
4848 getRepo,
4949 getReposByIds,
5050 listReposVisibleTo,
51 withoutArchived,
5152 type Repo,
5253 getRepoById,
5354 } from "../../core/repos.ts";
@@ -201,12 +202,14 @@ export function dashboardScreen(f: Session, ctx: Ctx): Screen {
201202 const orgs = f.user
202203 ? listOrgsForUser(f.db, f.user.id).map((o) => o.username)
203204 : [];
205 // Archived ones leave Yours as they leave the web home; the filter still finds them.
206 const live = withoutArchived(all);
204207 repos = f.user
205 ? all.filter(
208 ? live.filter(
206209 (repo) =>
207210 repo.ownerId === f.user?.id || orgs.includes(repo.ownerName),
208211 )
209 : all;
212 : live;
210213 ctx.invalidate();
211214 };
212215
@@ -240,6 +243,7 @@ export function dashboardScreen(f: Session, ctx: Ctx): Screen {
240243 const repo = byId.get(issue.repoId);
241244 if (
242245 repo === undefined ||
246 repo.archivedAt !== null ||
243247 !canRead(f.db, repo, f.user) ||
244248 !mayView(f.db, repo, issue, f.user)
245249 )
test/tui-archive.test.ts +32
@@ -6,6 +6,8 @@ import { tmpdir } from "node:os";
66 import { join } from "node:path";
77 import { updateRepoSettings } from "../src/actions/repos.ts";
88 import { createRepo } from "../src/actions/repos.ts";
9 import { openIssue } from "../src/actions/issues.ts";
10 import { setAssignee } from "../src/core/issues.ts";
911 import { signUp } from "../src/actions/users.ts";
1012 import { createApp, type App as Forge } from "../src/app.ts";
1113 import { getRepo, type Repo } from "../src/core/repos.ts";
@@ -111,4 +113,34 @@ describe("archive in the terminal", () => {
111113 h.app.stop();
112114 }
113115 });
116
117 test("the home leaves an archived repository out of Yours and On you", async () => {
118 const live = await createRepo(forge, alice, { name: "livelyrepo", visibility: "public" });
119 const old = await createRepo(forge, alice, { name: "oldrepo", visibility: "public" });
120 if (!live.ok || !old.ok) throw new Error("repo failed");
121 for (const [r, title] of [
122 [live.value, "livelytask"],
123 [old.value, "oldtask"],
124 ] as const) {
125 const issue = await openIssue(forge, alice, r, { title, body: "x" });
126 if (!issue.ok) throw new Error("issue failed");
127 setAssignee(forge.db, issue.value.id, alice.id);
128 }
129 await updateRepoSettings(forge, alice, old.value, { archived: true });
130 const h = boot({
131 sshUsername: "alice",
132 remoteAddress: "127.0.0.1",
133 fingerprint: "SHA256:test",
134 user: alice,
135 });
136 try {
137 await settleUntil(h.app, "livelyrepo");
138 const text = h.text();
139 expect(text).toContain("livelytask");
140 expect(text).not.toContain("alice/oldrepo");
141 expect(text).not.toContain("oldtask");
142 } finally {
143 h.app.stop();
144 }
145 });
114146 });

↑↓ navigate ↵ open ⌘↵ its app esc close

Keyboard shortcuts

Docs