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
Check · Review · Land · Live done — Landed and live in production.
scout/forge:scout/10140-automate-a-review-finding-as-a-lint-or-t → main 2 commits fork
4 files
changed +50
−17
scripts/header-width-allow.json +25 −1
| @@ -1,12 +1,36 @@ | ||
| 1 | 1 | [ |
| 2 | 2 | "scripts/cold-read.ts", |
| 3 | "src/actions/model_grant.ts", | |
| 4 | "src/actions/pinned.ts", | |
| 3 | 5 | "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", | |
| 4 | 20 | "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", | |
| 5 | 25 | "test/hot-reads-indexed.test.ts", |
| 6 | 26 | "test/metrics-cells.test.ts", |
| 7 | 27 | "test/pull-actions.test.ts", |
| 8 | 28 | "test/pull-quiet.test.ts", |
| 29 | "test/route-timing-coverage.test.ts", | |
| 30 | "test/shared-red.test.ts", | |
| 9 | 31 | "test/shelf-forty.test.ts", |
| 32 | "test/spawn-cell.test.ts", | |
| 10 | 33 | "test/thread-render-timing.test.ts", |
| 11 | "test/traffic-truth.test.ts" | |
| 34 | "test/traffic-truth.test.ts", | |
| 35 | "test/vitals.test.ts" | |
| 12 | 36 | ] |
scripts/header-width.ts +20 −12
| @@ -1,8 +1,9 @@ | ||
| 1 | 1 | /** |
| 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` | |
| 6 | 7 | * and only leave it: a new offender fails, and so does a listed file that |
| 7 | 8 | * is now clean, so the list shrinks and never grows. |
| 8 | 9 | */ |
| @@ -11,15 +12,22 @@ import { Glob } from "bun"; | ||
| 11 | 12 | |
| 12 | 13 | export const MAX_WIDTH = 160; |
| 13 | 14 | |
| 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 | */ | |
| 15 | 21 | export function longHeaderLines(file: string, text: string): string[] { |
| 16 | const lines = text.split("\n"); | |
| 17 | 22 | 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 | }); | |
| 23 | 31 | return out; |
| 24 | 32 | } |
| 25 | 33 | |
| @@ -36,6 +44,6 @@ export async function offenders(root = "."): Promise<Map<string, string[]>> { | ||
| 36 | 44 | if (import.meta.main) { |
| 37 | 45 | const allowed = new Set<string>(await Bun.file("scripts/header-width-allow.json").json()); |
| 38 | 46 | 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(", ")}`); | |
| 40 | 48 | process.exit(bad.length ? 1 : 0); |
| 41 | 49 | } |
scripts/unreached-allow.json −1
| @@ -1,7 +1,6 @@ | ||
| 1 | 1 | [ |
| 2 | 2 | "src/apps/cloud.ts", |
| 3 | 3 | "src/boards/machine.ts", |
| 4 | "src/core/teams_words.ts", | |
| 5 | 4 | "src/crew/governor_log.ts", |
| 6 | 5 | "src/crew/tries.ts", |
| 7 | 6 | "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", () => { | ||
| 6 | 6 | const long = `/**\n * ${"word ".repeat(40)}\n */\nconst x = 1;\n`; |
| 7 | 7 | expect(longHeaderLines("a.ts", long)).toEqual(["a.ts:2"]); |
| 8 | 8 | 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([]); | |
| 12 | 14 | }); |
| 13 | 15 | |
| 14 | 16 | test("rule header-width: wrap a file's header comment to the width; the allow list only shrinks", async () => { |
new above
offline