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

An issue body's `Files:` line, when present, is the only list of files the hot-file hold reads #6481

Merged opened this

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

Merged

scout/forge:scout/6475-an-issue-body-s-files-line-when-present → main 1 commit fork 1 thread · 1 unresolved

  • Merged. scout/6475-an-issue-body-s-files-line-when-present was merged into main as 7f5845d.

Review

Sign in to review this change.
3 files changed +23 −2
AGENTS.md +2
@@ -3,3 +3,5 @@
33 Read `CLAUDE.md` first: it is the rulebook for agents editing this repo.
44
55 README.md is capped: a new feature's how-to goes in `docs/features/<name>.md`, not a new README section.
6
7 When you write an issue, end it with a `Files:` line naming the files the work touches (`Files: src/a.ts, docs/b.md`). The hot-file hold reads only that line when it is there; without it, every path the issue mentions counts.

Non-blocking: a bare file name with no directory counts only when it is in backticks, the same rule as before. Planners already write Files: …, AGENTS.md, … without backticks, and #6475's own Files: line does. With this change that line is the only list read, so AGENTS.md silently drops out of the hold. A follow-up could say "bare names in backticks" here, or accept bare Name.ext tokens on the Files: line only.

src/crew/hot_files.ts +5 −2
@@ -6,7 +6,9 @@
66 * drains under the limit.
77 *
88 * - An issue's expected files are the repository paths its title and body
9 * name (`expectedFiles`): `src/http/mcp.ts`, `docs/MCP.md`.
9 * name (`expectedFiles`): `src/http/mcp.ts`, `docs/MCP.md`. When the body
10 * has a line that starts with `Files:`, only the paths on that line count;
11 * a path named elsewhere as context does not hold the build.
1012 * - The files an open pull touches are read from git once per head
1113 * (`learnPullFiles`, asked by the picker before it sorts), and kept in
1214 * memory: a cache git rebuilds, never state of its own.
@@ -40,7 +42,8 @@ const QUOTED = /`([\w-][\w.-]*\.[A-Za-z0-9]+)`/g;
4042
4143 /** The repository paths an issue names, in the order it names them, each once. */
4244 export function expectedFiles(issue: Pick<Issue, "title" | "body">): string[] {
43 const text = `${issue.title}\n${issue.body}`;
45 const listed = issue.body.match(/^Files:(.*)$/m);
46 const text = listed ? listed[1]! : `${issue.title}\n${issue.body}`;
4447 const found = new Set<string>();
4548 for (const pattern of [PATH, QUOTED])
4649 for (const match of text.matchAll(pattern)) {
test/hot-files-expected.test.ts added +16
@@ -0,0 +1,16 @@
1 import { expect, test } from "bun:test";
2 import { expectedFiles } from "../src/crew/hot_files.ts";
3
4 test("a Files: line is the only list of expected files", () => {
5 const body = "See src/actions/pulls.ts for context, and `README.md`.\n\nFiles: src/crew/hot_files.ts, `forge.json`\n\nAlso src/other/x.ts.";
6 expect(expectedFiles({ title: "Touch docs/MCP.md", body })).toEqual(["src/crew/hot_files.ts", "forge.json"]);
7 });
8
9 test("without a Files: line every mentioned path counts", () => {
10 const body = "See src/actions/pulls.ts and `README.md`.";
11 expect(expectedFiles({ title: "Touch docs/MCP.md", body })).toEqual(["docs/MCP.md", "src/actions/pulls.ts", "README.md"]);
12 });
13
14 test("an empty Files: line expects nothing", () => {
15 expect(expectedFiles({ title: "Touch docs/MCP.md", body: "See src/actions/pulls.ts.\nFiles:" })).toEqual([]);
16 });

↑↓ navigate ↵ open ⌘↵ its app esc close

Keyboard shortcuts

Docs