π fix: normalize lane prefixes in PR titles - #7239
Conversation
Move leading [lane] prefixes to the end of PR titles when the remainder is already a Conventional Commits header, preserving the agent record without breaking target repository title gates. Backfill the PR-title prompt guidance into remaining PR-capable defaults and pin it with tests. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Signed-off-by: Andrew Anderson <andy@clubanderson.com>
|
[APPROVALNOTIFIER] This PR is NOT APPROVED This pull-request has been approved by: The full list of commands accepted by this bot can be found here. DetailsNeeds approval from an approver in each of these files:Approvers can indicate their approval by writing |
Quality review: coverage gap at the choke point (test attached)Coverage check on this branch: However, the single wiring line this PR argues for β Suggested test (verified on this branch)Append to // Wiring: handleOnePRRequest must pass the request title through
// NormalizePRTitle before POSTing, so a lane-prefixed Conventional Commits
// title reaches GitHub with the lane moved to the end. This pins the
// server-side choke point itself, not just the pure function.
func TestPRRequestWatcher_NormalizesLanePrefixedTitle(t *testing.T) {
var postedTitle string
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
switch {
case r.Method == "GET" && strings.HasSuffix(r.URL.Path, "/repos/o/r"):
w.Header().Set("Content-Type", "application/json")
_, _ = io.WriteString(w, `{"name":"r","default_branch":"main"}`)
case r.Method == "GET" && strings.Contains(r.URL.Path, "/compare/"):
w.Header().Set("Content-Type", "application/json")
_, _ = io.WriteString(w, `{"files":[]}`)
case r.Method == "GET" && strings.HasSuffix(r.URL.Path, "/pulls"):
_, _ = io.WriteString(w, `[]`)
case r.Method == "GET" && strings.Contains(r.URL.Path, "/issues/"):
w.Header().Set("Content-Type", "application/json")
_, _ = io.WriteString(w, `{"number":1,"title":"ordinary issue","body":"implement the requested change","state":"open"}`)
case r.Method == "POST" && strings.HasSuffix(r.URL.Path, "/pulls"):
body, _ := io.ReadAll(r.Body)
var np map[string]any
_ = json.Unmarshal(body, &np)
postedTitle = asString(np["title"])
w.Header().Set("Content-Type", "application/json")
_, _ = io.WriteString(w, `{"number":42,"html_url":"https://github.com/o/r/pull/42"}`)
default:
w.WriteHeader(200)
_, _ = io.WriteString(w, `{}`)
}
}))
defer srv.Close()
c := testClient(t, srv.URL)
dir := t.TempDir()
old := prRequestDirForTest
prRequestDirForTest = dir
defer func() { prRequestDirForTest = old }()
_, err := WritePRRequest(dir, PRRequest{Repo: "o/r", Head: "scanner/fix-title", Title: "[scanner] fix(ci): retry transient GHCR errors", Body: "Fixes #1", Agent: "scanner"})
if err != nil {
t.Fatal(err)
}
c.ProcessPRRequestsOnce(context.Background())
want := "fix(ci): retry transient GHCR errors [scanner]"
if postedTitle != want {
t.Fatalf("title POSTed to GitHub = %q, want %q", postedTitle, want)
}
}Validation on
Not opening a separate PR since these files belong to this open PR's ground β applying the snippet here is mechanical. Quality agent (hold-gated mode). β hive: agent=quality backend=copilot model=claude-fable-5 copilot=1.0.78 |
|
Thank you for your contribution! Your PR has been merged. We'd love to hear how your experience was: share feedback |
Summary
This fixes agent-authored PR titles that are dead on arrival in repositories enforcing Conventional Commits from the first character.
ProjectBluefin has two representative gates:
projectbluefin/commonruns^(feat|fix|chore|docs|style|refactor|perf|test|ci|build|revert)(\(.+\))?(!)?: .+against the whole PR title. A leading[from[scanner]fails before the validfix(ci): ...header can be considered.projectbluefin/reviewusesamannn/action-semantic-pull-request@v6.1.1, whose parser starts at^(\w*)(?:\((.*)\))?!?: (.*)$;[is not a word character, so the header fails to parse.The server now normalizes only PR titles shaped like
[lane] <valid Conventional Commits header>into<valid Conventional Commits header> [lane]. Moving the lane to the end keeps it in the PR title for attribution and preserves it through squash-merge history, while making the type token start at position 0 for both gate styles.Why code, not another prompt tweak
#7159 / commit c361986 already added prose saying the lane prefix is not used for PRs, but prose did not hold: custom dashboard prompt overrides can shadow the default policy, and
projectbluefin/review#614still opened with a prefix after that change was live. The normalization is therefore enforced inpkg/githubat thehive-open-prrequest watcher, the server-side choke point agents cannot bypass.Scope
[lane]remains required for issues because issue routing still depends on the prefix.-fulltemplates selected by the ACMM packs andscanner-automerge.md.Validation