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

Automate a review finding as a lint or test: This line runs far past the file's wrap width; rewrap it. #10149

Merged opened this

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

Merged

scout/forge:scout/10140-automate-a-review-finding-as-a-lint-or-t → main 2 commits fork

  • Merged. scout/10140-automate-a-review-finding-as-a-lint-or-t was merged into main as 8c8371f.

Review

Sign in to review this change.
4 files changed +50 −17
scripts/header-width-allow.json +25 −1
@@ -1,12 +1,36 @@
11 [
22 "scripts/cold-read.ts",
3 "src/actions/model_grant.ts",
4 "src/actions/pinned.ts",
35 "src/boards/starting.ts",
6 "src/checks/scan_rules.ts",
7 "src/core/runs.ts",
8 "src/crew/brief.ts",
9 "src/crew/hot_history.ts",
10 "src/crew/week_read.ts",
11 "src/flow/hot_trend.ts",
12 "src/flow/keeper.ts",
13 "src/flow/kpis.ts",
14 "src/flows/view.ts",
15 "src/main.ts",
16 "src/packs/deploy.ts",
17 "src/tui/screens/resilience.ts",
18 "src/web/studio/home_cache.ts",
19 "test/app-tell-fit.test.ts",
420 "test/ask-to-live-batched.test.ts",
21 "test/cut-wave.test.ts",
22 "test/flow-public-only.test.ts",
23 "test/forge-webspec.test.ts",
24 "test/home-frame.test.ts",
525 "test/hot-reads-indexed.test.ts",
626 "test/metrics-cells.test.ts",
727 "test/pull-actions.test.ts",
828 "test/pull-quiet.test.ts",
29 "test/route-timing-coverage.test.ts",
30 "test/shared-red.test.ts",
931 "test/shelf-forty.test.ts",
32 "test/spawn-cell.test.ts",
1033 "test/thread-render-timing.test.ts",
11 "test/traffic-truth.test.ts"
34 "test/traffic-truth.test.ts",
35 "test/vitals.test.ts"
1236 ]
scripts/header-width.ts +20 −12
@@ -1,8 +1,9 @@
11 /**
2 * Rule `header-width`: a file's leading `/** … *​/` comment keeps every line
3 * within MAX_WIDTH columns. Reviewers kept asking to "wrap the header comment,
4 * it is now one very long line" (#8162, #8084, #9716, #8264); this says it
5 * for them (#9960). Files already over are listed in `header-width-allow.json`
2 * Rule `header-width`: a file's comments (the leading block and every comment-only
3 * line after it) keep every line within MAX_WIDTH columns. Reviewers kept
4 * asking to "wrap the header comment, it is now one very long line" (#8162,
5 * #8084, #9716, #8264) and to rewrap long comment lines (#8957, #9222, #9627,
6 * #9943); this says it for them (#9960, #10140). Files already over are listed in `header-width-allow.json`
67 * and only leave it: a new offender fails, and so does a listed file that
78 * is now clean, so the list shrinks and never grows.
89 */
@@ -11,15 +12,22 @@ import { Glob } from "bun";
1112
1213 export const MAX_WIDTH = 160;
1314
14 /** Lines of the leading block comment longer than MAX_WIDTH, as `file:line`. */
15 /**
16 * Lines of comment longer than MAX_WIDTH, as `file:line`: the leading block
17 * and every later comment-only line (`//` or inside a block comment), the
18 * "this comment line runs far past the others" finding (#8957, #9222, #9627,
19 * #9943). A line holding a URL is exempt: it cannot be wrapped.
20 */
1521 export function longHeaderLines(file: string, text: string): string[] {
16 const lines = text.split("\n");
1722 const out: string[] = [];
18 if (!lines[0]?.startsWith("/**")) return out;
19 for (let i = 0; i < lines.length; i++) {
20 if (lines[i]!.length > MAX_WIDTH) out.push(`${file}:${i + 1}`);
21 if (lines[i]!.trimEnd().endsWith("*/")) break;
22 }
23 let inBlock = false;
24 text.split("\n").forEach((line, i) => {
25 const t = line.trim();
26 const starts = inBlock || t.startsWith("//") || t.startsWith("/*");
27 if (!inBlock && t.startsWith("/*") && !t.includes("*/")) inBlock = true;
28 else if (inBlock && t.includes("*/")) inBlock = false;
29 if (starts && line.length > MAX_WIDTH && !/https?:\/\//.test(line)) out.push(`${file}:${i + 1}`);
30 });
2331 return out;
2432 }
2533
@@ -36,6 +44,6 @@ export async function offenders(root = "."): Promise<Map<string, string[]>> {
3644 if (import.meta.main) {
3745 const allowed = new Set<string>(await Bun.file("scripts/header-width-allow.json").json());
3846 const bad = [...(await offenders())].filter(([f]) => !allowed.has(f));
39 for (const [, hits] of bad) console.error(`header-width: wrap to ${MAX_WIDTH} columns: ${hits.join(", ")}`);
47 for (const [, hits] of bad) console.error(`header-width: comment line over ${MAX_WIDTH} columns, rewrap it: ${hits.join(", ")}`);
4048 process.exit(bad.length ? 1 : 0);
4149 }
scripts/unreached-allow.json −1
@@ -1,7 +1,6 @@
11 [
22 "src/apps/cloud.ts",
33 "src/boards/machine.ts",
4 "src/core/teams_words.ts",
54 "src/crew/governor_log.ts",
65 "src/crew/tries.ts",
76 "src/flow/alarm_drift.ts",
test/header-width.test.ts +5 −3
@@ -6,9 +6,11 @@ test("a header line over the width is named, a wrapped one is not", () => {
66 const long = `/**\n * ${"word ".repeat(40)}\n */\nconst x = 1;\n`;
77 expect(longHeaderLines("a.ts", long)).toEqual(["a.ts:2"]);
88 expect(longHeaderLines("a.ts", "/**\n * short\n */\n")).toEqual([]);
9 // only the leading comment counts; code after it does not
10 expect(longHeaderLines("a.ts", `/**\n * ok\n */\n// ${"x".repeat(200)}\n`)).toEqual([]);
11 expect(longHeaderLines("a.ts", `// ${"x".repeat(200)}\n`)).toEqual([]);
9 // later comment-only lines count (#8957, #9222, #9627, #9943); code and URLs do not
10 expect(longHeaderLines("a.ts", `/**\n * ok\n */\n// ${"x ".repeat(100)}\n`)).toEqual(["a.ts:4"]);
11 expect(longHeaderLines("a.ts", `x();\n /* ${"x ".repeat(100)}\n ${"y ".repeat(100)}*/\n`)).toEqual(["a.ts:2", "a.ts:3"]);
12 expect(longHeaderLines("a.ts", `const s = "${"x".repeat(200)}";\n`)).toEqual([]);
13 expect(longHeaderLines("a.ts", `// see https://e.com/${"x".repeat(200)}\n`)).toEqual([]);
1214 });
1315
1416 test("rule header-width: wrap a file's header comment to the width; the allow list only shrinks", async () => {

↑↓ navigate ↵ open ⌘↵ its app esc close

Keyboard shortcuts

Docs