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

The page manifest offers "make it yours" on a repository with an app, so an agent finds the copy without being taught #7863

Merged opened this

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

Merged

scout/forge:scout/7854-the-page-manifest-offers-make-it-yours-o → main 1 commit fork 1 thread · 1 unresolved

  • Merged. scout/7854-the-page-manifest-offers-make-it-yours-o was merged into main as 2a29f9c.

Review

Sign in to review this change.
2 files changed +46 −0
src/actions/here.ts +13
@@ -360,6 +360,19 @@ function repoActions(
360360 const record = getAppByRepo(db, repo.id);
361361 if (record !== undefined && canEnterApp(db, repo, record, viewer))
362362 out.push({ do: "open the app", http: `GET ${r}/app/production/open` });
363 // The copy is for whoever cannot write the app (the web's button, `!canWrite`);
364 // its dry run is `ro`, so a read-only token sees it too.
365 if (
366 record !== undefined &&
367 canEnterApp(db, repo, record, viewer) &&
368 !canWrite(db, repo, viewer)

Non-blocking: record !== undefined && canEnterApp(...) repeats the check on the line above. Compute const enter = ... once and use it for both actions.

369 )
370 out.push({
371 do: "make it yours",
372 http: `POST ${r}/make-it-yours`,
373 mcp: "make_it_yours",
374 ...(viewer === undefined ? { account: true } : unproven),
375 });
363376 if (!acts) return out;
364377 const tellSsh = sshTellCommand(app);
365378 if (mayDirectCrew(db, repo, viewer))
test/here-manifest.test.ts +33
@@ -77,6 +77,18 @@ describe("the manifest: what you can do here, as who you are", () => {
7777 visibility: "private",
7878 });
7979 if (!vault.ok) throw new Error(String(vault.error));
80 const pub = await newApp(app, alice, {
81 name: "pub",
82 owner: "acme",
83 visibility: "public",
84 });
85 if (!pub.ok) throw new Error(String(pub.error));
86 const plain = await createRepo(app, alice, {
87 name: "plain",
88 owner: "acme",
89 visibility: "public",
90 });
91 if (!plain.ok) throw new Error(String(plain.error));
8092 const bot = await createAgent(app, alice, { username: "bot" });
8193 if (!bot.ok) throw new Error(String(bot.error));
8294 expect(addToCrew(app, alice, game.value.repo, "bot").ok).toBe(true);
@@ -323,6 +335,27 @@ describe("the manifest: what you can do here, as who you are", () => {
323335 expect(carol.actions.find((a: any) => a.do === "offer a change").verify).toBeUndefined();
324336 });
325337
338 test("a repository with an app offers make it yours to whoever cannot write it", async () => {
339 const find = (m: any) => m.actions.find((a: any) => a.do === "make it yours");
340 const carol = find((await manifest("/acme/pub", "carol")).body);
341 expect(carol).toEqual({
342 do: "make it yours",
343 http: "POST /api/v1/repos/acme/pub/make-it-yours",
344 mcp: "make_it_yours",
345 });
346 expect(find((await manifest("/acme/pub", "una")).body).verify).toBe(true);
347 const out = find((await manifest("/acme/pub")).body);
348 expect(out.account).toBe(true);
349 expect(out.verify).toBeUndefined();
350 });
351
352 test("a writer, and a repository without an app, are not offered make it yours", async () => {
353 expect(does((await manifest("/acme/pub", "wes")).body)).not.toContain("make it yours");
354 expect(does((await manifest("/acme/pub", "alice")).body)).not.toContain("make it yours");
355 expect(does((await manifest("/acme/plain", "carol")).body)).not.toContain("make it yours");
356 expect(does((await manifest("/acme/plain")).body)).not.toContain("make it yours");
357 });
358
326359 test("an org's own pages are the org's, not a missing repository", async () => {
327360 const res = await here("/acme/-/teams/Writers", "alice");
328361 expect(res.status).toBe(200);

↑↓ navigate ↵ open ⌘↵ its app esc close

Keyboard shortcuts

Docs