diff --git a/AGENTS.md b/AGENTS.md index 798c0aeb..07447e1a 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -10,6 +10,7 @@ Read `CONTRIBUTING.md` before changing anything. Every rule in it applies to age 3. Write the message as UTF-8 without a byte-order mark, to a file outside the repository. 4. Check it with `python3 scripts/check_text_hygiene.py message `. 5. Commit with `git commit -F ` once the check passes. +- If the scope list printed in step 2 has more than three entries, the message check skips the scope: summarize the touched areas yourself, for example `(...)`. - On Windows, run `python` instead of `python3`. - Do not copy the format of older commits. Much of the history before October 2026 breaks these rules. - When you brief a sub-agent, give it the exact commit format and these rules, not a paraphrase. diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index aeed41ad..9a623a1b 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -29,7 +29,7 @@ Commands below use `python3`. On Windows, run `python` instead. - **Scope** is each changed path without a leading `MobileGL/`, cut to its first two parts: `MG_Backend/DirectGLES`, `MG_Test/Wire`, `scripts/ci`, `docs/Disaggregated`. - A shorter path is used whole: `MG_Backend` for a file directly in a subsystem, `scripts` for a file directly in `scripts/`, and the file name for a file at the repository root (`CMakeLists.txt`). - A file directly in `MobileGL/` uses `MobileGL`, and a submodule uses its path (`3rdparty/glslang`). - - List every scope the commit touches, comma-separated: `(MG_Backend/DirectGLES, MG_Test/Wire)`. A commit that touches more than three scopes may write `(...)` instead. + - List every scope the commit touches, comma-separated: `(MG_Backend/DirectGLES, MG_Test/Wire)`. A commit that touches more than three scopes skips this check: summarize the touched areas yourself, for example `(...)`. - `python3 scripts/check_text_hygiene.py scope` prints the scope for the staged changes. - **No** planning ids (phase or decision codes such as `P13` or `ID-54`), local paths, external project names or non-English text anywhere in the message. - **Merges** use `[Merge] (area): what the merge takes`. The scope is free text there. Write the subject yourself, since git's default merge message fails the check. The commits a merge brings in are not checked again. diff --git a/scripts/check_text_hygiene.py b/scripts/check_text_hygiene.py index 229c0281..04c7c9ab 100644 --- a/scripts/check_text_hygiene.py +++ b/scripts/check_text_hygiene.py @@ -50,7 +50,7 @@ _spec.loader.exec_module(HEADERS) TYPES = {"Feat", "Fix", "Perf", "Refactor", "Test", "Docs", "Build", "CI", "Chore", "Tools"} MERGE_TYPE = "Merge" CONTENT_MAX = 72 -# A commit that touches more scopes than this may write the scope as `(...)`. +# A commit that touches more scopes than this has its scope left to the author. SCOPE_ABBREVIATE_OVER = 3 CONTINUATION_MAX = 2 COMMENT_BLOCK_MAX = 15 @@ -176,11 +176,11 @@ def check_message(message, changed_paths, added_paths, submodules, is_merge=Fals if bad: problems.append("unknown type %s; use %s" % (", ".join(bad), ", ".join(sorted(TYPES)))) if changed_paths: - listed = [s.strip() for s in m.group("scopes").split(",")] expected = scopes_for(changed_paths, submodules) - abbreviated = listed == ["..."] and len(expected) > SCOPE_ABBREVIATE_OVER - if not abbreviated and sorted(listed, key=str.lower) != expected: - problems.append("scope should be (%s), not (%s)" % (", ".join(expected), m.group("scopes"))) + if len(expected) <= SCOPE_ABBREVIATE_OVER: + listed = [s.strip() for s in m.group("scopes").split(",")] + if sorted(listed, key=str.lower) != expected: + problems.append("scope should be (%s), not (%s)" % (", ".join(expected), m.group("scopes"))) content = m.group("content") if len(content) > CONTENT_MAX: @@ -709,9 +709,9 @@ def self_test(): expect("message rejects " + name, check_message(msg, paths, [], subs) != []) wide = ["MobileGL/MG_Backend/DirectGLES/a.cpp", "MobileGL/MG_Test/Wire/b.cpp", "MobileGL/MG_Impl/GLImpl/c.cpp", "MobileGL/MG_Remote/Client/d.cpp"] - expect("message: wide scope abbreviated", - check_message("[Chore] (...): touch four layers\n", wide, [], subs) == []) - expect("message: narrow scope not abbreviated", + expect("message: wide scope unchecked", + check_message("[Chore] (backend and tests): touch four layers\n", wide, [], subs) == []) + expect("message: narrow scope checked", check_message("[Chore] (...): touch two layers\n", paths, [], subs) != []) expect("message rejects new notes", check_message("[Docs] (docs/Disaggregated): add a review\n", ["docs/Disaggregated/notes/p9/r.md"],