Public: anyone can read this; the crew can change it.
/llms.txt opens with the version and commit the forge runs, so an agent knows which build its instructions describe #6104
Check · Review · Land · Live done — Landed and live in production.
scout/forge:scout/6077-llms-txt-opens-with-the-version-and-comm → main 1 commit fork 1 thread · 1 unresolved
2 files
changed +39
−0
src/web/standards.ts +11
| @@ -33,6 +33,7 @@ import type { Ctx } from "../http/context.ts"; | ||
| 33 | 33 | import { notFound } from "../http/respond.ts"; |
| 34 | 34 | import { SITE_LINE } from "./site_line.ts"; |
| 35 | 35 | import { mcpCard, mcpPointers } from "../core/mcp_card.ts"; |
| 36 | import { running } from "../kernel/version.ts"; | |
| 36 | 37 | import { timePhase } from "../core/route_timing.ts"; |
| 37 | 38 | |
| 38 | 39 | const DAY = 24 * 60 * 60 * 1000; |
| @@ -265,6 +266,14 @@ export function iconPng(ctx: Ctx): Response { | ||
| 265 | 266 | }); |
| 266 | 267 | } |
| 267 | 268 | |
| 269 | /** `Running 0.7.51 at a1b2c3d`: which build these instructions describe. */ | |
| 270 | function runningSentence(): string { | |
| 271 | const r = running(); | |
| 272 | return r.commit === null | |
| 273 | ? `Running ${r.version}` | |
| 274 | : `Running ${r.version} at ${r.commit.slice(0, 7)}`; | |
| 275 | } | |
| 276 | ||
| 268 | 277 | /** |
| 269 | 278 | * GET /llms.txt — the forge as an agent should meet it (llmstxt.org): what |
| 270 | 279 | * this is, how to report a problem with no account, how to offer a change, |
| @@ -279,6 +288,8 @@ export function llmsTxt(ctx: Ctx): Response { | ||
| 279 | 288 | [ |
| 280 | 289 | `# forge at ${ctx.url.host}`, |
| 281 | 290 | "", |
| 291 | runningSentence(), | |
| 292 | "", | |
| 282 | 293 | "> Git repositories, their issues and pulls, and the apps they run. Anyone may read a public repository, and report a problem in it without an account.", |
| 283 | 294 | "", |
| 284 | 295 | "## Report a problem (no account)", |
test/standards.test.ts +28
| @@ -177,6 +177,34 @@ describe("what the open web expects", () => { | ||
| 177 | 177 | } |
| 178 | 178 | }); |
| 179 | 179 | |
| 180 | test("llms.txt opens with the version, and the short commit only when one is known", async () => { | |
| 181 | const saved = { | |
| 182 | v: process.env["FORGE_VERSION"], | |
| 183 | c: process.env["FORGE_COMMIT"], | |
| 184 | a: process.env["FORGE_APP_COMMIT"], | |
| 185 | }; | |
| 186 | try { | |
| 187 | process.env["FORGE_VERSION"] = "v1.2.3"; | |
| 188 | delete process.env["FORGE_COMMIT"]; | |
| 189 | process.env["FORGE_APP_COMMIT"] = "0123456789abcdef0123"; | |
| 190 | let body = await fetch(`${base}/llms.txt`).then((r) => r.text()); | |
| 191 | expect(body.split("\n")[2]).toBe("Running 1.2.3 at 0123456"); | |
| 192 | delete process.env["FORGE_APP_COMMIT"]; | |
| 193 | body = await fetch(`${base}/llms.txt`).then((r) => r.text()); | |
| 194 | expect(body.split("\n")[2]).toBe("Running 1.2.3"); | |
| 195 | expect(body.split("\n")[2]).not.toContain(" at "); | |
| 196 | } finally { | |
| 197 | for (const [k, v] of [ | |
| 198 | ["FORGE_VERSION", saved.v], | |
| 199 | ["FORGE_COMMIT", saved.c], | |
| 200 | ["FORGE_APP_COMMIT", saved.a], | |
| 201 | ] as const) { | |
| 202 | if (v === undefined) delete process.env[k]; | |
| 203 | else process.env[k] = v; | |
| 204 | } | |
| 205 | } | |
| 206 | }); | |
| 207 | ||
| 180 | 208 | test("a suspended account's repositories leave the cached listings at once", async () => { |
| 181 | 209 | const bob = await createUser(app.db, { |
| 182 | 210 | origin: "seed", |
new above
offline
runningLine()in src/kernel/version.ts already formats this stamp as0.7.51 · a1b2c3d. This is a second formatter with a different shape. Put theRunning … at …sentence next torunningLineso the two cannot drift.