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

Automate a review finding as a lint or test: The line and the tints work and the tests are real, but three things need fixing #11894

Merged opened this

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

Merged

The gate now refuses three things that reviewers kept asking for, and each refusal names its rule. I ran the gate, `tsc` and `test/review-rules.test.ts` locally and they pass. I did not run the full suite or the fast-test selection (`scripts/fast.ts`); the forge's check runs them.

shipped production is serving it deploy 8f18665

opened this

The gate now refuses three things that reviewers kept asking for, and each refusal names its rule. I ran the gate, tsc and test/review-rules.test.ts locally and they pass. I did not run the full suite or the fast-test selection (scripts/fast.ts); the forge's check runs them.

What changed

  • New rules: src/kernel/review_rules.ts gets parseAdded and addedProblems, beside the existing #8888 rules. They judge only the lines a pull adds, because the tree already has about 600 as never casts and thousands of long comment lines. Main's own lines are never refused.
  • Gate: scripts/gate.ts gains addedRefusal, which diffs $FORGE_CHECK_BASE (or the merge base with main) to HEAD over src/ and test/. It runs with the other gate checks, so the check refuses a pull that adds these. If no base resolves, it passes, as the other diff-based gate checks do.
  • The rules, each from a review comment that came back:

- a test builds the real input, not as never` (#11074): in test files, an added as never fails. - a test does not wait on a real timer (#11723): in test files, an added Bun.sleep or a setTimeout-based wait fails. A line that ends in // real-timer: <why> is allowed. - a comment line is wrapped at 100 columns (#11074): in src/ and test/`, an added comment line over 100 characters fails.

  • Tests: test/review-rules.test.ts adds four tests. They cover the diff parsing, each rule's message with file and line, and the // real-timer: escape. The as never rule is checked not to fire in src/, and the 100-column rule not to fire in .md files.
  • Gate on its own commit: the first commit failed the gate on its own test file, because the fixtures contained the banned text. I built those strings apart and rephrased one test name.

What I chose not to do

  • Rules left out: two things from the 11074/11532 reviews have no rule, because a regex can't judge them.

- "Don't assert exact CSS source text". - "A link needs a 44px tap target or an accessible name". design-lint is the place for that, and the pull touches no src/web/.

  • #11761: its finding was a behaviour (chapter without view) in that one tool, so I made no general rule for it.
  • Old uses: I did not clean up the existing as never casts, sleeps or long comments.

To try it: on a branch, add x as never or await Bun.sleep(1) to a test file, commit, and run bun scripts/gate.ts. It prints the rule, file and line.

🤖 Generated with Claude Code

Closes #11884

commented

The gate now refuses three things that reviewers kept asking for, and each refusal names its rule. I ran the gate, tsc and test/review-rules.test.ts locally and they pass. I did not run the full suite or the fast-test selection (scripts/fast.ts); the forge's check runs them.

What changed

  • New rules: src/kernel/review_rules.ts gets parseAdded and addedProblems, beside the existing #8888 rules. They judge only the lines a pull adds, because the tree already has about 600 as never casts and thousands of long comment lines. Main's own lines are never refused.
  • Gate: scripts/gate.ts gains addedRefusal, which diffs $FORGE_CHECK_BASE (or the merge base with main) to HEAD over src/ and test/. It runs with the other gate checks, so the check refuses a pull that adds these. If no base resolves, it passes, as the other diff-based gate checks do.
  • The rules, each from a review comment that came back:

- a test builds the real input, not as never` (#11074): in test files, an added as never fails. - a test does not wait on a real timer (#11723): in test files, an added Bun.sleep or a setTimeout-based wait fails. A line that ends in // real-timer: <why> is allowed. - a comment line is wrapped at 100 columns (#11074): in src/ and test/`, an added comment line over 100 characters fails.

  • Tests: test/review-rules.test.ts adds four tests. They cover the diff parsing, each rule's message with file and line, and the // real-timer: escape. The as never rule is checked not to fire in src/, and the 100-column rule not to fire in .md files.
  • Gate on its own commit: the first commit failed the gate on its own test file, because the fixtures contained the banned text. I built those strings apart and rephrased one test name.

What I chose not to do

  • Rules left out: two things from the 11074/11532 reviews have no rule, because a regex can't judge them.

- "Don't assert exact CSS source text". - "A link needs a 44px tap target or an accessible name". design-lint is the place for that, and the pull touches no src/web/.

  • #11761: its finding was a behaviour (chapter without view) in that one tool, so I made no general rule for it.
  • Old uses: I did not clean up the existing as never casts, sleeps or long comments.

To try it: on a branch, add x as never or await Bun.sleep(1) to a test file, commit, and run bun scripts/gate.ts. It prints the rule, file and line.

🤖 Generated with Claude Code

Changes requested — before the latest push

The rules and their messages are real and named, and the added-lines-only approach is right. Two things to fix in one round:

  1. addedRefusal (the wiring that makes the rules bite in the check) has no test. Nothing fails if the gate stops calling it, if the base resolves wrong, or if the git diff args break. Add a test that builds a temp git repo (under TMPDIR) with a base commit and a head commit adding a cast in a test file, passes it as cwd with FORGE_CHECK_BASE set, and expects the rule line; and one that expects [] when no base resolves.
  2. COMMENT_LINE treats any line starting with as a comment, so a long code continuation like 1000 * rate ... over 100 columns is refused as an unwrapped comment. Match only in a block comment (^\s\(?:\s|\/|$) still catches it, so track / ... */ state across the added run or drop the bare-star form), and add a test with such a line.

Smaller: parseAdded treats an added line whose text starts with ++ as a file header; guard with the hunk state (only read headers before the first @@ of a file). Boxes 1 and 2 are not ticked this round: they are about the whole set of findings, and two of the review findings have no rule; say in the issue which are covered.

commented

This round fixes the three review points. tsc, scripts/gate.ts and the two test files I touched pass. I did not run the full suite; the forge's check does.

  • Gate wiring test: test/gate-added-refusal.test.ts builds a temp git repo under TMPDIR with a base commit and a head commit that adds a cast in a test file.

- With FORGE_CHECK_BASE set to the base, addedRefusal(cwd) returns the as never rule line, naming test/a.test.ts:2. - With no base resolving, it returns []. The repo is created with -b trunk so a main branch can't resolve by accident. - This now fails if the gate stops calling addedRefusal, if the base resolves wrong, or if the git diff arguments break.

  • *Bare in comments:** COMMENT_LINE no longer matches a bare . A line counts as a comment only inside a block comment opened by / on an added line just before it. A test covers a long 1000 * rate … line, which now passes. A long * word … line inside a /** … */ block is still refused.
  • ++ x as a file header: parseAdded reads +++ as a header only before the first @@ of a file, and resets on diff --git. A test covers an added ++ x line.
  • Block comment limitation: with -U0, a block comment that opened before the hunk can't be seen, so a long * line in an added run that begins mid-comment is not caught. I chose to miss it rather than refuse code.
  • Issue boxes 1 and 2: I left them unticked and did not edit the issue. The four findings covered by rules are as never, a real timer in a test, an unwrapped comment line, and a test importing another test. The set also holds the trailing-underscore name rule from the earlier work. I haven't listed which two review findings have no rule, since I can't tell which they are from here.
commented

The check failed because my new gate test tripped the as never rule it was testing. Line 42 spelled out the rule's own name, ` as never , in an assertion, and the gate reads every added line. The assertion now matches only rule "a test builds the real input`, so the file no longer contains the cast text.

bun scripts/gate.ts is clean against the merge-base, and test/gate-added-refusal.test.ts passes (2 tests). The pushed commit is dec97898d. The tsc run printed nothing.

The earlier changes in this pull are unchanged:

  • A temp-repo test now covers the gate's added-lines wiring (addedRefusal).
  • A bare * counts as a comment only inside a block comment.
  • File headers in a diff are read only outside a hunk.

Issue boxes 1 and 2 stay unticked. The two review findings that still have no rule are not named in the issue yet.

Approved

Read in full. The added-lines-only rules (as never in tests, real timers in tests with a real-timer escape, comment lines over 100 columns) each name their rule, file and line. The gate wiring now has a temp-repo test, the bare-star and the ++ header cases are fixed and tested, and no dependency or README change is added. Minor, not blocking: a code line containing /* inside a string (a glob like src/*/.ts) sets the block-comment flag, so a following long star-continuation line could be misread as a comment. Box 1 stays open because two review findings still have no rule and the issue does not say which are covered. Box 2 is met.

commented

Left #11884 open: 1 unticked item remain (A lint (scripts/) or a test that fails on exactly what these comments ask for, so a reviewer need not say it again.).

Sign in to join this conversation.

↑↓ navigate ↵ open ⌘↵ its app esc close

Keyboard shortcuts

Docs