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

A new app's own instructions promise a check the forge never runs #203

Merged opened this

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

Merged

scout/forge:scout/192-a-new-app-s-own-instructions-promise-a-c → main 1 commit fork

  • Merged. scout/192-a-new-app-s-own-instructions-promise-a-c was merged into main as 752a3dc.

Review

Sign in to review this change.
2 files changed +46 −3
src/apps/scaffold.ts +10 −3
@@ -89,11 +89,15 @@ makes "production data" and "preview data" two different things.
8989
9090 \`\`\`sh
9191 PORT=8080 FORGE_APP_DATA=./data bun run src/server.ts # run it here
92 bun test # the check every pull runs
92 bun test # what \`forge.json\`'s app.test runs on every push
9393 FORGE_APP_DATA=./data bun run src/tty.ts # the terminal
9494 FORGE_APP_DATA=./data bun run src/db.ts --check # schema version, integrity
9595 \`\`\`
9696
97 \`app.test\` in \`forge.json\` runs beside \`app.run\` in every deploy, not before
98 it: the preview comes up the moment the app answers, and says \`live\`, or
99 \`serving · tests failed\` the moment the suite disagrees.
100
97101 ## The shape
98102
99103 One implementation, many doors. \`src/actions.ts\` is every operation as a
@@ -178,8 +182,11 @@ preview at its own address, deployed from your branch as you push.
178182 - Work on the branch you were given. You cannot move the default branch;
179183 a person merges your pull once it is reviewed.
180184 - Keep state under \`FORGE_APP_DATA\`. Never write elsewhere at runtime.
181 - Run \`bun test\` before you push. It is the check the pull shows, and every
182 behaviour you add gets a test that would fail without it.
185 - Run \`bun test\` before you push. \`forge.json\`'s \`app.test\` runs it beside
186 the app on every deploy, not before it: the preview comes up the moment
187 the app answers and says \`live\`, or \`serving · tests failed\` the moment
188 the suite disagrees. Every behaviour you add gets a test that would fail
189 without it.
183190 - \`src/tty.ts\` is the app's terminal. The exact command that reaches it is
184191 \`FORGE_APP_SSH\` at runtime; print that verbatim wherever the app tells
185192 people how to connect. What a person can do on the page they can do here,
test/apps.test.ts +36
@@ -74,6 +74,8 @@ async function sh(args: string[], cwd: string) {
7474 }
7575
7676 import { identityKey } from "../src/apps/proxy.ts";
77 import { scaffoldFiles } from "../src/apps/scaffold.ts";
78 import { parseCheck } from "../src/apps/manifest.ts";
7779 describe("apps: production, previews, data, gates, crew", () => {
7880 let dataDir: string;
7981 let work: string;
@@ -1430,3 +1432,37 @@ describe("New app lands on the app's page, not the flash (issue #133)", () => {
14301432 expect(anonymous.status).toBe(404);
14311433 });
14321434 });
1435
1436 describe("a new app's own words promise only the check the forge runs (issue #192)", () => {
1437 const files = scaffoldFiles({ name: "hello", description: "A place for notes" });
1438 const content = (path: string): string => {
1439 const file = files.find((f) => f.path === path);
1440 if (file === undefined || typeof file.content !== "string")
1441 throw new Error(`scaffold has no text file at ${path}`);
1442 return file.content;
1443 };
1444
1445 // A sentence that talks about both "check" and "pull" is a sentence
1446 // claiming the forge checks the pull — true only when forge.json declares
1447 // one (`check`), since an app's own `test` runs beside its deploy instead.
1448 const promisesACheck = (prose: string): boolean =>
1449 prose
1450 .split(/(?<=[.!?])\s+/)
1451 .some((sentence) => /\bcheck/i.test(sentence) && /\bpull/i.test(sentence));
1452
1453 test("forge.json declares no separate check, and the prose does not claim one", () => {
1454 const checked = parseCheck(content("forge.json"), content("package.json"));
1455 expect(checked.ok).toBe(true);
1456 const declaresCheck = checked.ok && checked.value !== undefined;
1457 expect(declaresCheck).toBe(false);
1458 expect(promisesACheck(content("README.md"))).toBe(declaresCheck);
1459 expect(promisesACheck(content("AGENTS.md"))).toBe(declaresCheck);
1460 });
1461
1462 test("the README and AGENTS.md agree on what app.test actually does", () => {
1463 for (const path of ["README.md", "AGENTS.md"]) {
1464 expect(content(path)).toContain("app.test");
1465 expect(content(path)).toMatch(/beside/);
1466 }
1467 });
1468 });

↑↓ navigate ↵ open ⌘↵ its app esc close

Keyboard shortcuts

Docs