[bug-yhg8dqypwmar] fix(check-naming): require scoped titles #10

Merged
architect merged 3 commits from architect/bug-yhg8dqypwmar/require-conventional-titles into main 2026-08-27 18:54:18 +00:00
4 changed files with 29 additions and 17 deletions
+9 -5
View File
@@ -11,7 +11,7 @@ action fails (exits 1) on any violation; `@v1` is the legacy warn-only release
``` ```
Branch: <role>/bug-<id>/<kebab-description> e.g. dev/bug-x7k2m9/fix-terminal-resize Branch: <role>/bug-<id>/<kebab-description> e.g. dev/bug-x7k2m9/fix-terminal-resize
Chore: chore/<kebab-description> bug-less trivia only (dep bumps, typos, CI tweaks) Chore: chore/<kebab-description> bug-less trivia only (dep bumps, typos, CI tweaks)
Title: [bug-x7k2m9] Fix terminal resize loss (chore PRs: no [bug-id] prefix) Title: [bug-x7k2m9] fix(terminal): preserve resize state
Review

Behavior item 2 still says Bug-backed titles only need the [bug-id] prefix, while this head rejects that documented form. Update it to require [bug-id] type(scope): description and state whether ! is supported; otherwise this required gate blocks compliant-looking PRs.

Behavior item 2 still says Bug-backed titles only need the `[bug-id] ` prefix, while this head rejects that documented form. Update it to require `[bug-id] type(scope): description` and state whether `!` is supported; otherwise this required gate blocks compliant-looking PRs.
Review

You consult Behavior after this new example, but lines 76–78 still say only the [bug-id] prefix is required. Define the mandatory lowercase type, one-component scope, optional !, and non-empty description there; the explicit contract currently sends authors to a title CI rejects.

You consult Behavior after this new example, but lines 76–78 still say only the `[bug-id]` prefix is required. Define the mandatory lowercase type, one-component scope, optional `!`, and non-empty description there; the explicit contract currently sends authors to a title CI rejects.
Review

This example changes, but Behavior item 2 still promises that [bug-id] alone is valid. Update that operator contract to require the scoped Conventional Commit form and document optional !; otherwise a documented-valid title fails the required job.

This example changes, but Behavior item 2 still promises that `[bug-id] ` alone is valid. Update that operator contract to require the scoped Conventional Commit form and document optional `!`; otherwise a documented-valid title fails the required job.
``` ```
- `<role>` must be a roster handle: `dev` | `ux` | `ops` | `security` | `perf` | `architect` | `support` - `<role>` must be a roster handle: `dev` | `ux` | `ops` | `security` | `perf` | `architect` | `support`
@@ -48,7 +48,8 @@ jobs:
The explicit `edited` activity is part of the enforcement contract: changing The explicit `edited` activity is part of the enforcement contract: changing
the PR description must issue a new naming status for the same commit. the PR description must issue a new naming status for the same commit.
Description-only events may skip unrelated validation jobs. Keep required code-validation jobs in a separate workflow that does not run on
`edited`; Gitea treats a skipped required context as passing.
To wire a new repo warn-only first, add `continue-on-error: true` to the To wire a new repo warn-only first, add `continue-on-error: true` to the
job — the step still fails, but the job cannot block the PR. job — the step still fails, but the job cannot block the PR.
@@ -73,9 +74,12 @@ The check validates four things for every non-break-glass Agent PR:
Unknown roles, missing `bug-` segment, uppercase bug-ids, and empty Unknown roles, missing `bug-` segment, uppercase bug-ids, and empty
kebab descriptions all produce a `FAIL[check-naming]` log line. kebab descriptions all produce a `FAIL[check-naming]` log line.
2. **Title form** — for a `role/bug` branch the title must start with 2. **Title form** — for a `role/bug` branch the title must be
`[bug-<id>] `. For a `chore` branch the title must have no `[bug-id]` `[bug-<id>] type(scope): description`. The type starts with a lowercase
prefix. If both carry a bug-id they must match. letter and the type and one-component scope contain only lowercase letters,
digits, and hyphens. An optional `!` may follow the scope, and the description
must contain a non-whitespace character. For a `chore` branch the title must
have no `[bug-id]` prefix. If both carry a bug-id they must match.
3. **Tracking and attribution** — on Bug-backed work, `## Tracking` contains both the literal 3. **Tracking and attribution** — on Bug-backed work, `## Tracking` contains both the literal
`Fixes bug-<id>` automation token and the matching navigable `Fixes bug-<id>` automation token and the matching navigable
+1 -1
View File
@@ -8,7 +8,7 @@ description: |
Standard: Standard:
branch: <role>/bug-<id>/<kebab> e.g. dev/bug-x7k2m9/fix-terminal-resize branch: <role>/bug-<id>/<kebab> e.g. dev/bug-x7k2m9/fix-terminal-resize
chore: chore/<kebab> bug-less trivia only chore: chore/<kebab> bug-less trivia only
title: [bug-<id>] Description (chore PRs: no [bug-id] prefix) title: [bug-<id>] type(scope): description
Role must be a roster handle: dev | ux | ops | security | perf | architect | support Role must be a roster handle: dev | ux | ops | security | perf | architect | support
Break-glass: PRs authored by dfritz are exempt. Break-glass: PRs authored by dfritz are exempt.
+4 -1
View File
@@ -125,11 +125,14 @@ if [ "${BRANCH_KIND}" = "role-bug" ]; then
if [ -z "${TITLE_BUG}" ]; then if [ -z "${TITLE_BUG}" ]; then
echo "FAIL[check-naming]: title must lead with [${BRANCH_BUG}]" echo "FAIL[check-naming]: title must lead with [${BRANCH_BUG}]"
echo " have: ${TITLE}" echo " have: ${TITLE}"
echo " want: [${BRANCH_BUG}] ${TITLE}" echo " want: [${BRANCH_BUG}] fix(scope): description"
FAILED=1 FAILED=1
elif [ "${TITLE_BUG}" != "${BRANCH_BUG}" ]; then elif [ "${TITLE_BUG}" != "${BRANCH_BUG}" ]; then
echo "FAIL[check-naming]: bug-id mismatch — branch carries '${BRANCH_BUG}' but title carries '${TITLE_BUG}'" echo "FAIL[check-naming]: bug-id mismatch — branch carries '${BRANCH_BUG}' but title carries '${TITLE_BUG}'"
FAILED=1 FAILED=1
elif ! printf '%s\n' "${TITLE}" | grep -qE "^\\[${BRANCH_BUG}\\] [a-z][a-z0-9-]*\\([a-z0-9][a-z0-9-]*\\)!?: .*[^[:space:]]$"; then
Outdated
Review

You enter Fix resize; lines 125–128 tell you to change it to [bug-x7k2m9] Fix resize, which this new branch rejects on the next run. Make the first diagnostic show a valid scoped title, so recovery takes one edit and one rerun.

You enter `Fix resize`; lines 125–128 tell you to change it to `[bug-x7k2m9] Fix resize`, which this new branch rejects on the next run. Make the first diagnostic show a valid scoped title, so recovery takes one edit and one rerun.
Outdated
Review

You submit [bug-x7k2m9] fix(hub): and this regex accepts the spaces after : as a description; I reproduced exit 0. Require at least one non-whitespace description character and cover the empty/whitespace case.

You submit `[bug-x7k2m9] fix(hub): ` and this regex accepts the spaces after `: ` as a description; I reproduced exit 0. Require at least one non-whitespace description character and cover the empty/whitespace case.
echo "FAIL[check-naming]: Bug-backed title must use '[${BRANCH_BUG}] type(scope): description'"
FAILED=1
fi fi
tracking=$(printf '%s\n' "${VISIBLE_BODY}" | awk ' tracking=$(printf '%s\n' "${VISIBLE_BODY}" | awk '
+15 -10
View File
@@ -48,7 +48,7 @@ check_contract() {
local desc="$1" body="$2" base="$3" head="$4" diagnostic="$5" local desc="$1" body="$2" base="$3" head="$4" diagnostic="$5"
local out rc=0 local out rc=0
out=$(cd "${FIXTURES}" && HEAD_BRANCH="architect/bug-x7k2m9/contract" \ out=$(cd "${FIXTURES}" && HEAD_BRANCH="architect/bug-x7k2m9/contract" \
PR_TITLE="[bug-x7k2m9] Enforce contract" PR_AUTHOR="architect" \ PR_TITLE="[bug-x7k2m9] fix(check): enforce contract" PR_AUTHOR="architect" \
PR_BODY="$body" GITEA_SERVER_URL="https://code.test" GITEA_TOKEN="test-token" \ PR_BODY="$body" GITEA_SERVER_URL="https://code.test" GITEA_TOKEN="test-token" \
GITEA_REPOSITORY="action/check-naming" GITEA_PR_NUMBER=1 PATH="${TEST_BIN}:$PATH" \ GITEA_REPOSITORY="action/check-naming" GITEA_PR_NUMBER=1 PATH="${TEST_BIN}:$PATH" \
BASE_SHA="$base" HEAD_SHA="$head" bash "$SCRIPT" 2>&1) || rc=$? BASE_SHA="$base" HEAD_SHA="$head" bash "$SCRIPT" 2>&1) || rc=$?
@@ -66,7 +66,7 @@ check_contract_pass() {
local desc="$1" body="$2" base="$3" head="$4" local desc="$1" body="$2" base="$3" head="$4"
local out rc=0 local out rc=0
out=$(cd "${FIXTURES}" && HEAD_BRANCH="architect/bug-x7k2m9/contract" \ out=$(cd "${FIXTURES}" && HEAD_BRANCH="architect/bug-x7k2m9/contract" \
PR_TITLE="[bug-x7k2m9] Enforce contract" PR_AUTHOR="architect" \ PR_TITLE="[bug-x7k2m9] fix(check): enforce contract" PR_AUTHOR="architect" \
PR_BODY="$body" GITEA_SERVER_URL="https://code.test" GITEA_TOKEN="test-token" \ PR_BODY="$body" GITEA_SERVER_URL="https://code.test" GITEA_TOKEN="test-token" \
GITEA_REPOSITORY="action/check-naming" GITEA_PR_NUMBER=1 PATH="${TEST_BIN}:$PATH" \ GITEA_REPOSITORY="action/check-naming" GITEA_PR_NUMBER=1 PATH="${TEST_BIN}:$PATH" \
BASE_SHA="$base" HEAD_SHA="$head" bash "$SCRIPT" 2>&1) || rc=$? BASE_SHA="$base" HEAD_SHA="$head" bash "$SCRIPT" 2>&1) || rc=$?
@@ -103,13 +103,13 @@ check "dfritz exempt — invalid branch" 0 "totally/wrong-branch" "no prefix"
check "dfritz exempt — mismatch" 0 "dev/bug-abc/thing" "[bug-xyz] Thing" "dfritz" check "dfritz exempt — mismatch" 0 "dev/bug-abc/thing" "[bug-xyz] Thing" "dfritz"
# valid role/bug — all roster handles # valid role/bug — all roster handles
check "dev/bug matching title" 0 "dev/bug-x7k2m9/fix-terminal-resize" "[bug-x7k2m9] Fix terminal resize" "dev" check "dev/bug matching title" 0 "dev/bug-x7k2m9/fix-terminal-resize" "[bug-x7k2m9] fix(terminal): preserve resize" "dev"
check "architect/bug matching title" 0 "architect/bug-abc123/refactor-auth" "[bug-abc123] Refactor auth" "architect" check "architect/bug matching title" 0 "architect/bug-abc123/refactor-auth" "[bug-abc123] refactor(auth): simplify boundary" "architect"
check "ops/bug matching title" 0 "ops/bug-zz9/deploy-tweak" "[bug-zz9] Deploy tweak" "ops" check "ops/bug matching title" 0 "ops/bug-zz9/deploy-tweak" "[bug-zz9] fix(delivery): preserve rollout" "ops"
check "security/bug matching title" 0 "security/bug-s1a2/patch-cve" "[bug-s1a2] Patch CVE" "security" check "security/bug matching title" 0 "security/bug-s1a2/patch-cve" "[bug-s1a2] fix(auth): close bypass" "security"
check "perf/bug matching title" 0 "perf/bug-p0p0/reduce-latency" "[bug-p0p0] Reduce latency" "perf" check "perf/bug matching title" 0 "perf/bug-p0p0/reduce-latency" "[bug-p0p0] perf(checker): reduce latency" "perf"
check "ux/bug matching title" 0 "ux/bug-u1u1/polish-modal" "[bug-u1u1] Polish modal" "ux" check "ux/bug matching title" 0 "ux/bug-u1u1/polish-modal" "[bug-u1u1] fix(modal): preserve focus" "ux"
check "support/bug matching title" 0 "support/bug-sup9/clarify-error" "[bug-sup9] Clarify error" "support" check "support/bug matching title" 0 "support/bug-sup9/clarify-error" "[bug-sup9] docs(error): clarify recovery" "support"
# valid chore form # valid chore form
check "chore — no title prefix" 0 "chore/bump-deps" "Bump dependency versions" "dev" check "chore — no title prefix" 0 "chore/bump-deps" "Bump dependency versions" "dev"
@@ -124,6 +124,11 @@ check "bug-id mismatch" 1 "dev/bug-x7k2m9/fix-resize" "[bug-
# fail: chore branch with [bug-id] title prefix # fail: chore branch with [bug-id] title prefix
check "chore with bug-id title" 1 "chore/bump-deps" "[bug-abc123] Bump deps" "dev" check "chore with bug-id title" 1 "chore/bump-deps" "[bug-abc123] Bump deps" "dev"
check "role/bug Conventional title" 0 "dev/bug-x7k2m9/fix-resize" "[bug-x7k2m9] fix(hub): fix resize" "dev" check "role/bug Conventional title" 0 "dev/bug-x7k2m9/fix-resize" "[bug-x7k2m9] fix(hub): fix resize" "dev"
check "role/bug plain title" 1 "dev/bug-x7k2m9/fix-resize" "[bug-x7k2m9] Fix resize" "dev"
check "role/bug missing scope" 1 "dev/bug-x7k2m9/fix-resize" "[bug-x7k2m9] fix: fix resize" "dev"
check "role/bug path scope" 1 "dev/bug-x7k2m9/fix-resize" "[bug-x7k2m9] fix(hub/bugs): fix resize" "dev"
check "role/bug empty description" 1 "dev/bug-x7k2m9/fix-resize" "[bug-x7k2m9] fix(hub): " "dev"
check "role/bug whitespace description" 1 "dev/bug-x7k2m9/fix-resize" "[bug-x7k2m9] fix(hub): " "dev"
check "chore Conventional title" 0 "chore/bump-deps" "chore(deps): bump deps" "dev" check "chore Conventional title" 0 "chore/bump-deps" "chore(deps): bump deps" "dev"
# fail: invalid branch forms # fail: invalid branch forms
@@ -214,7 +219,7 @@ canonical_body bug-x7k2m9 >> "${LARGE_BODY}"
large_out="" large_out=""
large_rc=0 large_rc=0
large_out=$(cd "${FIXTURES}" && HEAD_BRANCH="architect/bug-x7k2m9/contract" \ large_out=$(cd "${FIXTURES}" && HEAD_BRANCH="architect/bug-x7k2m9/contract" \
PR_TITLE="[bug-x7k2m9] Enforce contract" PR_AUTHOR="architect" \ PR_TITLE="[bug-x7k2m9] fix(check): enforce contract" PR_AUTHOR="architect" \
TEST_PR_BODY_FILE="${LARGE_BODY}" GITEA_SERVER_URL="https://code.test" GITEA_TOKEN="test-token" \ TEST_PR_BODY_FILE="${LARGE_BODY}" GITEA_SERVER_URL="https://code.test" GITEA_TOKEN="test-token" \
GITEA_REPOSITORY="action/check-naming" GITEA_PR_NUMBER=1 PATH="${TEST_BIN}:$PATH" \ GITEA_REPOSITORY="action/check-naming" GITEA_PR_NUMBER=1 PATH="${TEST_BIN}:$PATH" \
BASE_SHA="$BASE" HEAD_SHA="$GOOD_HEAD" bash "$SCRIPT" 2>&1) || large_rc=$? BASE_SHA="$BASE" HEAD_SHA="$GOOD_HEAD" bash "$SCRIPT" 2>&1) || large_rc=$?